com php-src: Merge sapi/phpdbg into PHP-5.6: sapi/phpdbg/.travis.yml sapi/phpdbg/config.m4 sapi/phpdbg/phpdbg_utils.c sapi/php dbg/phpdbg_utils.h
sapi/phpdbg/tests/run-tests.php sa pi/phpdbg/travis/ci.sh
| From: | Bob Weinand | Date: | Sun, 15 Jun 2014 13:47:14 +0000 |
| Subject: | com php-src: Merge sapi/phpdbg into PHP-5.6: sapi/phpdbg/.travis.yml sapi/phpdbg/config.m4 sapi/phpdbg/phpdbg_utils.c sapi/php dbg/phpdbg_utils.h sapi/phpdbg/tests/run-tests.php sa pi/phpdbg/travis/ci.sh |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-78878@lists.php.net to get a copy of this message | ||
Commit: f677889a9a536d506febc13e1022eb4816fb5fa1
Author: Bob Weinand <bobwei9@hotmail.com> Sun, 15 Jun 2014 15:47:14 +0200
Parents: da3ef175b98266e8920c326ff619d70ef8c375c3 e9c2e7d6e630ae660b6b5dc7269af58d4bf7bf54
Branches: PHP-5.6
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=f677889a9a536d506febc13e1022eb4816fb5fa1
Log:
Merge sapi/phpdbg into PHP-5.6
Changed paths:
MA sapi/phpdbg/.travis.yml
MA sapi/phpdbg/config.m4
MA sapi/phpdbg/phpdbg_utils..c
MA sapi/phpdbg/phpdbg_utils.h
MA sapi/phpdbg/tests/run-tests.php
MA sapi/phpdbg/travis/ci.sh
diff --cc sapi/phpdbg/.travis.yml index 3534028,0000000..d5b492e mode 100644,000000..100644 --- a/sapi/phpdbg/.travis.yml +++ b/sapi/phpdbg/.travis.yml @@@ -1,3 -1,0 +1,12 @@@ +language: c + - script: ./travis/ci.sh ++env: ++- PHP="PHP-5.4" ++- PHP="PHP-5.5" ++- PHP="PHP-5.6" ++- PHP="master" ++ ++before_script: ./travis/ci.sh ++ ++script: ++- ./php-src/sapi/cli/php php-src/sapi/phpdbg/tests/run-tests.php -diff2stdout --phpdbg php-src/sapi/phpdbg/phpdbg diff --cc sapi/phpdbg/config.m4 index ecac171,0000000..1a6640e mode 100644,000000..100644 --- a/sapi/phpdbg/config.m4 +++ b/sapi/phpdbg/config.m4 @@@ -1,66 -1,0 +1,67 @@@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(phpdbg, for phpdbg support, +[ --enable-phpdbg Build phpdbg], no, no) + +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_HEADER_TIOCGWINSZ + 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_parser.c phpdbg_lexer.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 phpdbg_watch.c phpdbg_btree.c" + + if test "$PHP_READLINE" != "no"; then + PHPDBG_EXTRA_LIBS="$PHP_READLINE_LIBS" + fi + + PHP_SUBST(PHP_PHPDBG_CFLAGS) + PHP_SUBST(PHP_PHPDBG_FILES) + PHP_SUBST(PHPDBG_EXTRA_LIBS) + + PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag], [$abs_srcdir/sapi/phpdbg], [$abs_builddir/sapi/phpdbg]) + 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: diff --cc sapi/phpdbg/phpdbg_utils.c index c9b22a2,0000000..98748b2 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@@ -1,448 -1,0 +1,451 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 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" +#elif defined(HAVE_SYS_IOCTL_H) +# include "sys/ioctl.h" ++# ifndef GWINSZ_IN_SYS_IOCTL ++# include <termios.h> ++# endif +#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 +}; /* }}} */ + +/* {{{ */ +const static phpdbg_element_t elements[] = { + PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT), + PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR), + PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE), + PHPDBG_ELEMENT_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; + const time_t tt = tp.tv_sec; + + strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tt)); + 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 int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) { + const phpdbg_element_t *element = elements; + + while (element && element->name) { + if (len == element->name_length) { + if (strncasecmp(name, element->name, len) == SUCCESS) { + return element->id; + } + } + element++; + } + + return PHPDBG_COLOR_INVALID; +} + +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]; +} /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D) { + if (!EG(active_op_array)) { + phpdbg_error("No active op array!"); + return FAILURE; + } + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + + if (!EG(active_symbol_table)) { + phpdbg_error("No active symbol table!"); + return FAILURE; + } + } + + return SUCCESS; +} + +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */ +{ + int columns; +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; - #elif defined(HAVE_SYS_IOCTL_H) ++#elif defined(HAVE_SYS_IOCTL_H) && defined (TIOCGWINSZ) + struct winsize w; + - columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100; ++ columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 80; +#else - columns = 100; ++ columns = 80; +#endif + return columns; +} /* }}} */ diff --cc sapi/phpdbg/phpdbg_utils.h index 68ae7e4,0000000..56bacfc mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_utils.h +++ b/sapi/phpdbg/phpdbg_utils.h @@@ -1,127 -1,0 +1,147 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 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_ELEMENT_LEN 3 +#define PHPDBG_ELEMENT_D(name, id) \ + {name, sizeof(name)-1, id} +#define PHPDBG_ELEMENT_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; + +typedef struct _phpdbg_element_t { + char *name; + size_t name_length; + int id; +} phpdbg_element_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); +PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */ + +/* {{{ Prompt Management */ +PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC); +PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */ + +/* {{{ Console Width */ +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D); + ++#if PHP_VERSION_ID < 50500 ++/* copy from zend_hash.c PHP 5.5 for 5.4 compatibility */ ++static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { ++ Bucket *p; ++ ++ p = pos ? (*pos) : ht->pInternalPointer; ++ ++ if (!p) { ++ Z_TYPE_P(key) = IS_NULL; ++ } else if (p->nKeyLength) { ++ Z_TYPE_P(key) = IS_STRING; ++ Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1); ++ Z_STRLEN_P(key) = p->nKeyLength - 1; ++ } else { ++ Z_TYPE_P(key) = IS_LONG; ++ Z_LVAL_P(key) = p->h; ++ } ++} ++#endif ++ +#endif /* PHPDBG_UTILS_H */ diff --cc sapi/phpdbg/tests/run-tests.php index 47a998c,0000000..1cc31d8 mode 100644,000000..100644 --- a/sapi/phpdbg/tests/run-tests.php +++ b/sapi/phpdbg/tests/run-tests.php @@@ -1,595 -1,0 +1,597 @@@ +<?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; ++ 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) { ++ 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-diff2stdout\t\twrite diff to stdout instead of files%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); ++ $tests[] = new Test($this->config, $test); + } + } ++ ++ return $tests; + } + + /** + * + * @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); + + return $result; + } + + 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) { ++ 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; ++ $this->config = $config; ++ $this->file = $file; + } + } + + /** + * Obvious!! + * + */ + public function getResult() { - $options = sprintf( - '-i%s -nqb', $this->file); ++ $options = sprintf('-i%s -nqb', $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() { + if (count($this->diff['wants'])) { + if (!$this->config->hasFlag('nodiff')) { + if ($this->config->hasFlag('diff2stdout')) { + $difffile = "php://stdout"; + file_put_contents($difffile, "====DIFF====\n"); + } else { + $difffile = sprintf( + '%s/%s.diff', + dirname($this->file), basename($this->file)); + } + + if (($diff = fopen($difffile, '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) { ++ 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']; + + $retval = 0; + + { + $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) { + $retval |= !$tests->logTest($path, $test); + } + + $tests->logPathStats($path); + } + + $tests->logStats(); + } + + die($retval); +} +?> diff --cc sapi/phpdbg/travis/ci.sh index d9f3ac6,0000000..206b158 mode 100755,000000..100755 --- a/sapi/phpdbg/travis/ci.sh +++ b/sapi/phpdbg/travis/ci.sh @@@ -1,10 -1,0 +1,11 @@@ +#!/usr/bin/env sh +git clone https://github.com/php/php-src - cd php-src/sapi ++cd php-src ++git checkout $PHP ++cd sapi +rm -rf phpdbg +git clone https://github.com/krakjoe/phpdbg.git +cd ../ +./buildconf --force +./configure --disable-all --enable-phpdbg --enable-maintainer-zts +make - make test-phpdbg
diff --cc sapi/phpdbg/.travis.yml index 3534028,0000000..d5b492e mode 100644,000000..100644 --- a/sapi/phpdbg/.travis.yml +++ b/sapi/phpdbg/.travis.yml @@@ -1,3 -1,0 +1,12 @@@ +language: c + - script: ./travis/ci.sh ++env: ++- PHP="PHP-5.4" ++- PHP="PHP-5.5" ++- PHP="PHP-5.6" ++- PHP="master" ++ ++before_script: ./travis/ci.sh ++ ++script: ++- ./php-src/sapi/cli/php php-src/sapi/phpdbg/tests/run-tests.php -diff2stdout --phpdbg php-src/sapi/phpdbg/phpdbg diff --cc sapi/phpdbg/config.m4 index ecac171,0000000..1a6640e mode 100644,000000..100644 --- a/sapi/phpdbg/config.m4 +++ b/sapi/phpdbg/config.m4 @@@ -1,66 -1,0 +1,67 @@@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(phpdbg, for phpdbg support, +[ --enable-phpdbg Build phpdbg], no, no) + +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_HEADER_TIOCGWINSZ + 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_parser.c phpdbg_lexer.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 phpdbg_watch.c phpdbg_btree.c" + + if test "$PHP_READLINE" != "no"; then + PHPDBG_EXTRA_LIBS="$PHP_READLINE_LIBS" + fi + + PHP_SUBST(PHP_PHPDBG_CFLAGS) + PHP_SUBST(PHP_PHPDBG_FILES) + PHP_SUBST(PHPDBG_EXTRA_LIBS) + + PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag], [$abs_srcdir/sapi/phpdbg], [$abs_builddir/sapi/phpdbg]) + 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: diff --cc sapi/phpdbg/phpdbg_utils.c index c9b22a2,0000000..98748b2 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@@ -1,448 -1,0 +1,451 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 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" +#elif defined(HAVE_SYS_IOCTL_H) +# include "sys/ioctl.h" ++# ifndef GWINSZ_IN_SYS_IOCTL ++# include <termios.h> ++# endif +#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 +}; /* }}} */ + +/* {{{ */ +const static phpdbg_element_t elements[] = { + PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT), + PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR), + PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE), + PHPDBG_ELEMENT_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; + const time_t tt = tp.tv_sec; + + strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tt)); + 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 int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) { + const phpdbg_element_t *element = elements; + + while (element && element->name) { + if (len == element->name_length) { + if (strncasecmp(name, element->name, len) == SUCCESS) { + return element->id; + } + } + element++; + } + + return PHPDBG_COLOR_INVALID; +} + +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]; +} /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D) { + if (!EG(active_op_array)) { + phpdbg_error("No active op array!"); + return FAILURE; + } + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + + if (!EG(active_symbol_table)) { + phpdbg_error("No active symbol table!"); + return FAILURE; + } + } + + return SUCCESS; +} + +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */ +{ + int columns; +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; - #elif defined(HAVE_SYS_IOCTL_H) ++#elif defined(HAVE_SYS_IOCTL_H) && defined (TIOCGWINSZ) + struct winsize w; + - columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100; ++ columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 80; +#else - columns = 100; ++ columns = 80; +#endif + return columns; +} /* }}} */ diff --cc sapi/phpdbg/phpdbg_utils.h index 68ae7e4,0000000..56bacfc mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_utils.h +++ b/sapi/phpdbg/phpdbg_utils.h @@@ -1,127 -1,0 +1,147 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 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_ELEMENT_LEN 3 +#define PHPDBG_ELEMENT_D(name, id) \ + {name, sizeof(name)-1, id} +#define PHPDBG_ELEMENT_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; + +typedef struct _phpdbg_element_t { + char *name; + size_t name_length; + int id; +} phpdbg_element_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); +PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */ + +/* {{{ Prompt Management */ +PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC); +PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */ + +/* {{{ Console Width */ +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D); + ++#if PHP_VERSION_ID < 50500 ++/* copy from zend_hash.c PHP 5.5 for 5.4 compatibility */ ++static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { ++ Bucket *p; ++ ++ p = pos ? (*pos) : ht->pInternalPointer; ++ ++ if (!p) { ++ Z_TYPE_P(key) = IS_NULL; ++ } else if (p->nKeyLength) { ++ Z_TYPE_P(key) = IS_STRING; ++ Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1); ++ Z_STRLEN_P(key) = p->nKeyLength - 1; ++ } else { ++ Z_TYPE_P(key) = IS_LONG; ++ Z_LVAL_P(key) = p->h; ++ } ++} ++#endif ++ +#endif /* PHPDBG_UTILS_H */ diff --cc sapi/phpdbg/tests/run-tests.php index 47a998c,0000000..1cc31d8 mode 100644,000000..100644 --- a/sapi/phpdbg/tests/run-tests.php +++ b/sapi/phpdbg/tests/run-tests.php @@@ -1,595 -1,0 +1,597 @@@ +<?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; ++ 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) { ++ 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-diff2stdout\t\twrite diff to stdout instead of files%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); ++ $tests[] = new Test($this->config, $test); + } + } ++ ++ return $tests; + } + + /** + * + * @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); + + return $result; + } + + 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) { ++ 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; ++ $this->config = $config; ++ $this->file = $file; + } + } + + /** + * Obvious!! + * + */ + public function getResult() { - $options = sprintf( - '-i%s -nqb', $this->file); ++ $options = sprintf('-i%s -nqb', $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() { + if (count($this->diff['wants'])) { + if (!$this->config->hasFlag('nodiff')) { + if ($this->config->hasFlag('diff2stdout')) { + $difffile = "php://stdout"; + file_put_contents($difffile, "====DIFF====\n"); + } else { + $difffile = sprintf( + '%s/%s.diff', + dirname($this->file), basename($this->file)); + } + + if (($diff = fopen($difffile, '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) { ++ 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']; + + $retval = 0; + + { + $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) { + $retval |= !$tests->logTest($path, $test); + } + + $tests->logPathStats($path); + } + + $tests->logStats(); + } + + die($retval); +} +?> diff --cc sapi/phpdbg/travis/ci.sh index d9f3ac6,0000000..206b158 mode 100755,000000..100755 --- a/sapi/phpdbg/travis/ci.sh +++ b/sapi/phpdbg/travis/ci.sh @@@ -1,10 -1,0 +1,11 @@@ +#!/usr/bin/env sh +git clone https://github.com/php/php-src - cd php-src/sapi ++cd php-src ++git checkout $PHP ++cd sapi +rm -rf phpdbg +git clone https://github.com/krakjoe/phpdbg.git +cd ../ +./buildconf --force +./configure --disable-all --enable-phpdbg --enable-maintainer-zts +make - make test-phpdbg