Fix a bug when var_export() causes a fatal error that could inadvertently display data due to flushing of the output buffer.

Examples include, memory limit, execution time and recursion.
This commit is contained in:
Scott MacVicar 2010-07-09 21:11:37 +00:00
parent 5438a9d23c
commit c7b0abe6aa
4 changed files with 96 additions and 53 deletions

View file

@ -34,6 +34,8 @@ PHP_FUNCTION(memory_get_peak_usage);
PHPAPI void php_var_dump(zval **struc, int level TSRMLS_DC);
PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC);
PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC);
PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC);
typedef HashTable* php_serialize_data_t;

View file

@ -14,12 +14,5 @@ var_export($obj, true);
?>
===DONE===
--EXPECTF--
stdClass::__set_state(array(
'p' =>
stdClass::__set_state(array(
'p' =>
stdClass::__set_state(array(
'p' =>
stdClass::__set_state(array(
Fatal error: Nesting level too deep - recursive dependency? in %s on line 9

View file

@ -14,14 +14,5 @@ var_export($a, true);
?>
===DONE===
--EXPECTF--
array (
0 =>
array (
0 =>
array (
0 =>
array (
0 =>
array (
Fatal error: Nesting level too deep - recursive dependency? in %s on line 9

View file

@ -334,27 +334,48 @@ PHP_FUNCTION(debug_zval_dump)
}
/* }}} */
#define buffer_append_spaces(buf, num_spaces) \
do { \
char *tmp_spaces; \
int tmp_spaces_len; \
tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \
smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \
efree(tmp_spaces); \
} while(0);
static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
int level;
smart_str *buf;
level = va_arg(args, int);
buf = va_arg(args, smart_str *);
if (hash_key->nKeyLength == 0) { /* numeric key */
php_printf("%*c%ld => ", level + 1, ' ', hash_key->h);
buffer_append_spaces(buf, level+1);
smart_str_append_long(buf, hash_key->h);
smart_str_appendl(buf, " => ", 4);
} else { /* string key */
char *key, *tmp_str;
int key_len, tmp_len;
key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
php_printf("%*c'", level + 1, ' ');
PHPWRITE(tmp_str, tmp_len);
php_printf("' => ");
buffer_append_spaces(buf, level + 1);
smart_str_appendc(buf, '\'');
smart_str_appendl(buf, tmp_str, tmp_len);
smart_str_appendl(buf, "' => ", 5);
efree(key);
efree(tmp_str);
}
php_var_export(zv, level + 2 TSRMLS_CC);
PUTS (",\n");
php_var_export_ex(zv, level + 2, buf TSRMLS_CC);
smart_str_appendc(buf, ',');
smart_str_appendc(buf, '\n');
return 0;
}
/* }}} */
@ -362,24 +383,31 @@ static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list a
static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
int level;
smart_str *buf;
char *prop_name, *class_name;
level = va_arg(args, int);
buf = va_arg(args, smart_str *);
php_printf("%*c", level + 1, ' ');
buffer_append_spaces(buf, level + 2);
if (hash_key->nKeyLength != 0) {
zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name);
php_printf(" '%s' => ", prop_name);
smart_str_appendc(buf, '\'');
smart_str_appends(buf, prop_name);
smart_str_appendc(buf, '\'');
} else {
php_printf(" %ld => ", hash_key->h);
smart_str_append_long(buf, hash_key->h);
}
php_var_export(zv, level + 2 TSRMLS_CC);
PUTS (",\n");
smart_str_appendl(buf, " => ", 4);
php_var_export_ex(zv, level + 2, buf TSRMLS_CC);
smart_str_appendc(buf, ',');
smart_str_appendc(buf, '\n');
return 0;
}
/* }}} */
PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC) /* {{{ */
PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) /* {{{ */
{
HashTable *myht;
char *tmp_str, *tmp_str2;
@ -389,82 +417,111 @@ PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC) /* {{{ */
switch (Z_TYPE_PP(struc)) {
case IS_BOOL:
php_printf("%s", Z_LVAL_PP(struc) ? "true" : "false");
if (Z_LVAL_PP(struc)) {
smart_str_appendl(buf, "true", 4);
} else {
smart_str_appendl(buf, "false", 5);
}
break;
case IS_NULL:
php_printf("NULL");
smart_str_appendl(buf, "NULL", 4);
break;
case IS_LONG:
php_printf("%ld", Z_LVAL_PP(struc));
smart_str_append_long(buf, Z_LVAL_PP(struc));
break;
case IS_DOUBLE:
php_printf("%.*H", (int) EG(precision), Z_DVAL_PP(struc));
tmp_len = spprintf(&tmp_str, 0,"%.*H", (int) EG(precision), Z_DVAL_PP(struc));
smart_str_appendl(buf, tmp_str, tmp_len);
efree(tmp_str);
break;
case IS_STRING:
tmp_str = php_addcslashes(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &tmp_len, 0, "'\\", 2 TSRMLS_CC);
tmp_str2 = php_str_to_str_ex(tmp_str, tmp_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len2, 0, NULL);
PUTS ("'");
PHPWRITE(tmp_str2, tmp_len2);
PUTS ("'");
smart_str_appendc(buf, '\'');
smart_str_appendl(buf, tmp_str2, tmp_len2);
smart_str_appendc(buf, '\'');
efree(tmp_str2);
efree(tmp_str);
break;
case IS_ARRAY:
myht = Z_ARRVAL_PP(struc);
if (level > 1) {
php_printf("\n%*c", level - 1, ' ');
smart_str_appendc(buf, '\n');
buffer_append_spaces(buf, level - 1);
}
PUTS ("array (\n");
zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_array_element_export, 1, level, 0);
smart_str_appendl(buf, "array (\n", 8);
zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_array_element_export, 2, level, buf);
if (level > 1) {
php_printf("%*c", level - 1, ' ');
buffer_append_spaces(buf, level - 1);
}
PUTS(")");
smart_str_appendc(buf, ')');
break;
case IS_OBJECT:
myht = Z_OBJPROP_PP(struc);
if (level > 1) {
php_printf("\n%*c", level - 1, ' ');
smart_str_appendc(buf, '\n');
buffer_append_spaces(buf, level - 1);
}
Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC);
php_printf ("%s::__set_state(array(\n", class_name);
smart_str_appendl(buf, class_name, class_name_len);
smart_str_appendl(buf, "::__set_state(array(\n", 21);
efree(class_name);
if (myht) {
zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_object_element_export, 1, level);
zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_object_element_export, 1, level, buf);
}
if (level > 1) {
php_printf("%*c", level - 1, ' ');
buffer_append_spaces(buf, level - 1);
}
php_printf ("))");
smart_str_appendl(buf, "))", 2);
break;
default:
PUTS ("NULL");
smart_str_appendl(buf, "NULL", 4);
break;
}
}
/* }}} */
/* FOR BC reasons, this will always perform and then print */
PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC) /* {{{ */
{
smart_str buf = {0};
php_var_export_ex(struc, level, &buf TSRMLS_CC);
smart_str_0 (&buf);
PHPWRITE(buf.c, buf.len);
smart_str_free(&buf);
}
/* }}} */
/* {{{ proto mixed var_export(mixed var [, bool return])
Outputs or returns a string representation of a variable */
PHP_FUNCTION(var_export)
{
zval *var;
zend_bool return_output = 0;
smart_str buf = {0};
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &var, &return_output) == FAILURE) {
return;
}
if (return_output) {
php_output_start_default(TSRMLS_C);
}
php_var_export(&var, 1 TSRMLS_CC);
php_var_export_ex(&var, 1, &buf TSRMLS_CC);
smart_str_0 (&buf);
if (return_output) {
php_output_get_contents(return_value TSRMLS_CC);
php_output_discard(TSRMLS_C);
RETVAL_STRINGL(buf.c, buf.len, 1);
} else {
PHPWRITE(buf.c, buf.len);
}
smart_str_free(&buf);
}
/* }}} */