/* +----------------------------------------------------------------------+ | Xdebug | +----------------------------------------------------------------------+ | Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Derick Rethans | +----------------------------------------------------------------------+ | This source file is subject to version 1.0 of the Xdebug license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://xdebug.derickrethans.nl/license.php | | If you did not receive a copy of the Xdebug license and are unable | | to obtain it through the world-wide-web, please send a note to | | xdebug@derickrethans.nl so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Derick Rethans | +----------------------------------------------------------------------+ */ #include "php.h" #include "ext/standard/php_string.h" #include "ext/standard/url.h" #include "zend.h" #include "zend_extensions.h" #include "php_xdebug.h" #include "xdebug_compat.h" #include "xdebug_private.h" #include "xdebug_mm.h" #include "xdebug_var.h" #include "xdebug_xml.h" ZEND_EXTERN_MODULE_GLOBALS(xdebug) char* xdebug_error_type(int type) { switch (type) { case E_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: case E_USER_ERROR: return xdstrdup("Fatal error"); break; #if PHP_VERSION_ID >= 50200 case E_RECOVERABLE_ERROR: return xdstrdup("Catchable fatal error"); break; #endif case E_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: case E_USER_WARNING: return xdstrdup("Warning"); break; case E_PARSE: return xdstrdup("Parse error"); break; case E_NOTICE: case E_USER_NOTICE: return xdstrdup("Notice"); break; #ifdef ZEND_ENGINE_2 case E_STRICT: return xdstrdup("Strict standards"); break; #endif default: return xdstrdup("Unknown error"); break; } } /***************************************************************************** ** PHP Variable related utility functions */ zval* xdebug_get_php_symbol(char* name, int name_length) { HashTable *st = NULL; zval **retval; TSRMLS_FETCH(); st = XG(active_symbol_table); if (st && st->nNumOfElements && zend_hash_find(st, name, name_length, (void **) &retval) == SUCCESS) { return *retval; } st = EG(active_op_array)->static_variables; if (st) { if (zend_hash_find(st, name, name_length, (void **) &retval) == SUCCESS) { return *retval; } } st = &EG(symbol_table); if (zend_hash_find(st, name, name_length, (void **) &retval) == SUCCESS) { return *retval; } return NULL; } static char* xdebug_get_property_info(char *mangled_property, int mangled_len, char **property_name) { #ifdef ZEND_ENGINE_2 char *prop_name, *class_name; #if PHP_VERSION_ID >= 50200 zend_unmangle_property_name(mangled_property, mangled_len - 1, &class_name, &prop_name); #else zend_unmangle_property_name(mangled_property, &class_name, &prop_name); #endif *property_name = prop_name; if (class_name) { if (class_name[0] == '*') { return "protected"; } else { return "private"; } } else { return "public"; } #else *property_name = mangled_property; return "var"; #endif } static xdebug_var_export_options* get_options_from_ini(TSRMLS_D) { xdebug_var_export_options *options; options = xdmalloc(sizeof(xdebug_var_export_options)); options->max_children = XG(display_max_children); options->max_data = XG(display_max_data); options->max_depth = XG(display_max_depth); options->show_hidden = 0; if (options->max_children == -1) { options->max_children = 1048576; } else if (options->max_children < 1) { options->max_children = 1; } if (options->max_data == -1) { options->max_data = 1073741824; } else if (options->max_data < 1) { options->max_data = 1; } if (options->max_depth == -1) { options->max_depth = 4096; } else if (options->max_depth < 0) { options->max_depth = 0; } options->runtime = (xdebug_var_runtime_page*) xdmalloc((options->max_depth + 1) * sizeof(xdebug_var_runtime_page)); return options; } xdebug_var_export_options xdebug_var_nolimit_options = { 1048576, 1048576, 64, 1, NULL }; xdebug_var_export_options* xdebug_var_get_nolimit_options(TSRMLS_D) { return &xdebug_var_nolimit_options; } /***************************************************************************** ** Normal variable printing routines */ static int xdebug_array_element_export(zval **zv, int num_args, va_list args, zend_hash_key *hash_key) { int level, debug_zval; xdebug_str *str; xdebug_var_export_options *options; TSRMLS_FETCH(); level = va_arg(args, int); str = va_arg(args, struct xdebug_str*); debug_zval = va_arg(args, int); options = va_arg(args, xdebug_var_export_options*); if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { if (hash_key->nKeyLength==0) { /* numeric key */ xdebug_str_add(str, xdebug_sprintf("%ld => ", hash_key->h), 1); } else { /* string key */ int newlen = 0; char *tmp, *tmp2; tmp = php_str_to_str(hash_key->arKey, hash_key->nKeyLength, "'", 1, "\\'", 2, &newlen); tmp2 = php_str_to_str(tmp, newlen - 1, "\0", 1, "\\0", 2, &newlen); if (tmp) { efree(tmp); } xdebug_str_addl(str, "'", 1, 0); if (tmp2) { xdebug_str_addl(str, tmp2, newlen, 0); efree(tmp2); } xdebug_str_add(str, "' => ", 0); } xdebug_var_export(zv, str, level + 2, debug_zval, options TSRMLS_CC); xdebug_str_addl(str, ", ", 2, 0); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_addl(str, "..., ", 5, 0); } options->runtime[level].current_element_nr++; return 0; } static int xdebug_object_element_export(zval **zv, int num_args, va_list args, zend_hash_key *hash_key) { int level, debug_zval; xdebug_str *str; xdebug_var_export_options *options; char *prop_name, *modifier; TSRMLS_FETCH(); level = va_arg(args, int); str = va_arg(args, struct xdebug_str*); debug_zval = va_arg(args, int); options = va_arg(args, xdebug_var_export_options*); if (options->runtime[level].current_element_nr >= options->runtime[level].start_element_nr && options->runtime[level].current_element_nr < options->runtime[level].end_element_nr) { if (hash_key->nKeyLength != 0) { modifier = xdebug_get_property_info(hash_key->arKey, hash_key->nKeyLength, &prop_name); xdebug_str_add(str, xdebug_sprintf("%s $%s = ", modifier, prop_name), 1); } xdebug_var_export(zv, str, level + 2, debug_zval, options TSRMLS_CC); xdebug_str_addl(str, "; ", 2, 0); } if (options->runtime[level].current_element_nr == options->runtime[level].end_element_nr) { xdebug_str_addl(str, "...; ", 5, 0); } options->runtime[level].current_element_nr++; return 0; } void xdebug_var_export(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options TSRMLS_DC) { HashTable *myht; char* tmp_str; int tmp_len; if (!struc || !(*struc)) { return; } if (debug_zval) { xdebug_str_add(str, xdebug_sprintf("(refcount=%d, is_ref=%d)=", (*struc)->XDEBUG_REFCOUNT, (*struc)->XDEBUG_IS_REF), 1); } switch (Z_TYPE_PP(struc)) { case IS_BOOL: xdebug_str_add(str, xdebug_sprintf("%s", Z_LVAL_PP(struc) ? "TRUE" : "FALSE"), 1); break; case IS_NULL: xdebug_str_addl(str, "NULL", 4, 0); break; case IS_LONG: xdebug_str_add(str, xdebug_sprintf("%ld", Z_LVAL_PP(struc)), 1); break; case IS_DOUBLE: xdebug_str_add(str, xdebug_sprintf("%.*G", (int) EG(precision), Z_DVAL_PP(struc)), 1); break; case IS_STRING: tmp_str = php_addcslashes(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &tmp_len, 0, "'\\\0..\37", 6 TSRMLS_CC); if (options->max_data == 0 || Z_STRLEN_PP(struc) <= options->max_data) { xdebug_str_add(str, xdebug_sprintf("'%s'", tmp_str), 1); } else { xdebug_str_addl(str, "'", 1, 0); xdebug_str_addl(str, xdebug_sprintf("%s", tmp_str), options->max_data, 1); xdebug_str_addl(str, "...'", 4, 0); } efree(tmp_str); break; case IS_ARRAY: myht = Z_ARRVAL_PP(struc); if (myht->nApplyCount < 1) { xdebug_str_addl(str, "array (", 7, 0); if (level <= options->max_depth) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; zend_hash_apply_with_arguments(myht, (apply_func_args_t) xdebug_array_element_export, 4, level, str, debug_zval, options); /* Remove the ", " at the end of the string */ if (myht->nNumOfElements > 0) { xdebug_str_chop(str, 2); } } else { xdebug_str_addl(str, "...", 3, 0); } xdebug_str_addl(str, ")", 1, 0); } else { xdebug_str_addl(str, "...", 3, 0); } break; case IS_OBJECT: myht = Z_OBJPROP_PP(struc); if (myht->nApplyCount < 1) { xdebug_str_add(str, xdebug_sprintf("class %s { ", Z_OBJCE_PP(struc)->name), 1); if (level <= options->max_depth) { options->runtime[level].current_element_nr = 0; options->runtime[level].start_element_nr = 0; options->runtime[level].end_element_nr = options->max_children; zend_hash_apply_with_arguments(myht, (apply_func_args_t) xdebug_object_element_export, 4, level, str, debug_zval, options); /* Remove the ", " at the end of the string */ if (myht->nNumOfElements > 0) { xdebug_str_chop(str, 2); } } else { xdebug_str_addl(str, "...", 3, 0); } xdebug_str_addl(str, " }", 2, 0); } else { xdebug_str_addl(str, "...", 3, 0); } break; case IS_RESOURCE: { char *type_name; type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC); xdebug_str_add(str, xdebug_sprintf("resource(%ld) of type (%s)", Z_LVAL_PP(struc), type_name ? type_name : "Unknown"), 1); break; } default: xdebug_str_addl(str, "NULL", 4, 0); break; } } char* xdebug_get_zval_value(zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str str = {0, 0, NULL}; int default_options = 0; TSRMLS_FETCH(); if (!options) { options = get_options_from_ini(TSRMLS_C); default_options = 1; } xdebug_var_export(&val, (xdebug_str*) &str, 1, debug_zval, options TSRMLS_CC); if (default_options) { xdfree(options); } return str.d; } static void xdebug_var_synopsis(zval **struc, xdebug_str *str, int level, int debug_zval, xdebug_var_export_options *options TSRMLS_DC) { HashTable *myht; if (!struc || !(*struc)) { return; } if (debug_zval) { xdebug_str_add(str, xdebug_sprintf("(refcount=%d, is_ref=%d)=", (*struc)->XDEBUG_REFCOUNT, (*struc)->XDEBUG_IS_REF), 1); } switch (Z_TYPE_PP(struc)) { case IS_BOOL: xdebug_str_addl(str, "bool", 4, 0); break; case IS_NULL: xdebug_str_addl(str, "null", 4, 0); break; case IS_LONG: xdebug_str_addl(str, "long", 4, 0); break; case IS_DOUBLE: xdebug_str_addl(str, "double", 6, 0); break; case IS_STRING: xdebug_str_add(str, xdebug_sprintf("string(%d)", Z_STRLEN_PP(struc)), 1); break; case IS_ARRAY: myht = Z_ARRVAL_PP(struc); xdebug_str_add(str, xdebug_sprintf("array(%d)", myht->nNumOfElements), 1); break; case IS_OBJECT: xdebug_str_add(str, xdebug_sprintf("class %s", Z_OBJCE_PP(struc)->name), 1); break; case IS_RESOURCE: { char *type_name; type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC); xdebug_str_add(str, xdebug_sprintf("resource(%ld) of type (%s)", Z_LVAL_PP(struc), type_name ? type_name : "Unknown"), 1); break; } } } char* xdebug_get_zval_synopsis(zval *val, int debug_zval, xdebug_var_export_options *options) { xdebug_str str = {0, 0, NULL}; int default_options = 0; TSRMLS_FETCH(); if (!options) { options = get_options_from_ini(TSRMLS_C); default_options = 1; } xdebug_var_synopsis(&val, (xdebug_str*) &str, 1, debug_zval, options TSRMLS_CC); if (default_options) { xdfree(options); } return str.d; } /***************************************************************************** ** XML variable printing routines */ static int xdebug_array_element_export_xml(zval **zv, int num_args, va_list args, zend_hash_key *hash_key) { int level; xdebug_str *str; TSRMLS_FETCH(); level = va_arg(args, int); str = va_arg(args, struct xdebug_str*); xdebug_str_addl(str, "nKeyLength == 0) { /* numeric key */ xdebug_str_add(str, xdebug_sprintf(" name='%ld'", hash_key->h), 1); } else { /* string key */ xdebug_str_add(str, xdebug_sprintf(" name='%s'", hash_key->arKey), 1); } xdebug_str_add(str, xdebug_sprintf(" id='%p'>", *zv), 1); xdebug_var_export_xml(zv, str, level + 1 TSRMLS_CC); xdebug_str_addl(str, "", 6, 0); return 0; } static int xdebug_object_element_export_xml(zval **zv, int num_args, va_list args, zend_hash_key *hash_key) { int level; xdebug_str *str; char *prop_name, *modifier; TSRMLS_FETCH(); level = va_arg(args, int); str = va_arg(args, struct xdebug_str*); xdebug_str_addl(str, "nKeyLength != 0) { modifier = xdebug_get_property_info(hash_key->arKey, hash_key->nKeyLength, &prop_name); xdebug_str_add(str, xdebug_sprintf(" name='%s' facet='%s'", prop_name, modifier), 1); } xdebug_str_add(str, xdebug_sprintf(" id='%p'>", *zv), 1); xdebug_var_export_xml(zv, str, level + 1 TSRMLS_CC); xdebug_str_addl(str, "", 6, 0); return 0; } void xdebug_var_export_xml(zval **struc, xdebug_str *str, int level TSRMLS_DC) { HashTable *myht; char* tmp_str; int newlen; if (!*struc) { xdebug_str_addl(str, "", 16, 0); return; } switch (Z_TYPE_PP(struc)) { case IS_BOOL: xdebug_str_add(str, xdebug_sprintf("%s", Z_LVAL_PP(struc) ? "1" : "0"), 1); break; case IS_NULL: xdebug_str_addl(str, "", 7, 0); break; case IS_LONG: xdebug_str_add(str, xdebug_sprintf("%ld", Z_LVAL_PP(struc)), 1); break; case IS_DOUBLE: xdebug_str_add(str, xdebug_sprintf("%.*G", (int) EG(precision), Z_DVAL_PP(struc)), 1); break; case IS_STRING: xdebug_str_addl(str, "", 8, 0); tmp_str = xdebug_xmlize(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &newlen); xdebug_str_addl(str, tmp_str, newlen, 0); efree(tmp_str); xdebug_str_addl(str, "", 9, 0); break; case IS_ARRAY: myht = Z_ARRVAL_PP(struc); if (myht->nApplyCount < 1) { xdebug_str_addl(str, "", 7, 0); zend_hash_apply_with_arguments(myht, (apply_func_args_t) xdebug_array_element_export_xml, 2, level, str); xdebug_str_addl(str, "", 8, 0); } else { xdebug_str_addl(str, "