Skip to content

Commit 7fee8dd

Browse files
committed
Nicer printing of accessors in exception backtraces
1 parent ea327ea commit 7fee8dd

File tree

5 files changed

+199
-8
lines changed

5 files changed

+199
-8
lines changed
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
--TEST--
2+
debug_backtrace() from within accessor
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar {
8+
get { var_dump(debug_backtrace()); }
9+
set { var_dump(debug_backtrace()); }
10+
isset { var_dump(debug_backtrace()); }
11+
unset { var_dump(debug_backtrace()); }
12+
}
13+
}
14+
15+
$foo = new Foo;
16+
17+
$foo->bar;
18+
$foo->bar = 'test';
19+
isset($foo->bar);
20+
unset($foo->bar);
21+
22+
?>
23+
--EXPECTF--
24+
array(1) {
25+
[0]=>
26+
array(8) {
27+
["file"]=>
28+
string(%d) "%s"
29+
["line"]=>
30+
int(%d)
31+
["property"]=>
32+
string(3) "bar"
33+
["accessor"]=>
34+
string(3) "get"
35+
["class"]=>
36+
string(3) "Foo"
37+
["object"]=>
38+
object(Foo)#1 (1) {
39+
["bar"]=>
40+
NULL
41+
}
42+
["type"]=>
43+
string(2) "->"
44+
["args"]=>
45+
array(0) {
46+
}
47+
}
48+
}
49+
array(1) {
50+
[0]=>
51+
array(8) {
52+
["file"]=>
53+
string(%d) "%s"
54+
["line"]=>
55+
int(%d)
56+
["property"]=>
57+
string(3) "bar"
58+
["accessor"]=>
59+
string(3) "set"
60+
["class"]=>
61+
string(3) "Foo"
62+
["object"]=>
63+
object(Foo)#1 (1) {
64+
["bar"]=>
65+
NULL
66+
}
67+
["type"]=>
68+
string(2) "->"
69+
["args"]=>
70+
array(1) {
71+
[0]=>
72+
&string(4) "test"
73+
}
74+
}
75+
}
76+
array(1) {
77+
[0]=>
78+
array(8) {
79+
["file"]=>
80+
string(%d) "%s"
81+
["line"]=>
82+
int(%d)
83+
["property"]=>
84+
string(3) "bar"
85+
["accessor"]=>
86+
string(5) "isset"
87+
["class"]=>
88+
string(3) "Foo"
89+
["object"]=>
90+
object(Foo)#1 (1) {
91+
["bar"]=>
92+
NULL
93+
}
94+
["type"]=>
95+
string(2) "->"
96+
["args"]=>
97+
array(0) {
98+
}
99+
}
100+
}
101+
array(1) {
102+
[0]=>
103+
array(8) {
104+
["file"]=>
105+
string(%d) "%s"
106+
["line"]=>
107+
int(%d)
108+
["property"]=>
109+
string(3) "bar"
110+
["accessor"]=>
111+
string(5) "unset"
112+
["class"]=>
113+
string(3) "Foo"
114+
["object"]=>
115+
object(Foo)#1 (1) {
116+
["bar"]=>
117+
NULL
118+
}
119+
["type"]=>
120+
string(2) "->"
121+
["args"]=>
122+
array(0) {
123+
}
124+
}
125+
}

Zend/tests/accessors/exception.phpt

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Throwing an exception from an accessor
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar {
8+
get { throw new Exception('except in getter'); }
9+
set { throw new Exception('except in setter'); }
10+
isset { throw new Exception('except in issetter'); }
11+
unset { throw new Exception('except in unsetter'); }
12+
}
13+
}
14+
15+
$foo = new Foo;
16+
17+
try { $foo->bar; } catch (Exception $e) { echo $e, "\n\n"; }
18+
try { $foo->bar = 'test'; } catch (Exception $e) { echo $e, "\n\n"; }
19+
try { isset($foo->bar); } catch (Exception $e) { echo $e, "\n\n"; }
20+
try { unset($foo->bar); } catch (Exception $e) { echo $e, "\n\n"; }
21+
22+
?>
23+
--EXPECTF--
24+
exception 'Exception' with message 'except in getter' in %s:%d
25+
Stack trace:
26+
#0 %s(%d): Foo->bar->get()
27+
#1 {main}
28+
29+
exception 'Exception' with message 'except in setter' in %s:%d
30+
Stack trace:
31+
#0 %s(%d): Foo->bar->set('test')
32+
#1 {main}
33+
34+
exception 'Exception' with message 'except in issetter' in %s:%d
35+
Stack trace:
36+
#0 %s(%d): Foo->bar->isset()
37+
#1 {main}
38+
39+
exception 'Exception' with message 'except in unsetter' in %s:%d
40+
Stack trace:
41+
#0 %s(%d): Foo->bar->unset()
42+
#1 {main}

Zend/zend_builtin_functions.c

+19-6
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,7 @@ ZEND_FUNCTION(debug_print_backtrace)
21942194
ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit TSRMLS_DC)
21952195
{
21962196
zend_execute_data *ptr, *skip;
2197+
zend_function *fbc;
21972198
int lineno, frameno = 0;
21982199
const char *function_name;
21992200
const char *filename;
@@ -2260,14 +2261,26 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
22602261
filename = NULL;
22612262
}
22622263

2263-
function_name = ptr->function_state.function->common.function_name;
2264+
fbc = ptr->function_state.function;
2265+
function_name = fbc->common.function_name;
22642266

22652267
if (function_name) {
2266-
add_assoc_string_ex(stack_frame, "function", sizeof("function"), (char*)function_name, 1);
2268+
if (IS_ACCESSOR_FN(fbc)) {
2269+
add_assoc_string_ex(
2270+
stack_frame, "property", sizeof("property"),
2271+
zend_get_accessor_name_from_function(fbc TSRMLS_CC), 1
2272+
);
2273+
add_assoc_string_ex(
2274+
stack_frame, "accessor", sizeof("accessor"),
2275+
zend_fn_purpose_string(fbc), 1
2276+
);
2277+
} else {
2278+
add_assoc_string_ex(stack_frame, "function", sizeof("function"), (char*)function_name, 1);
2279+
}
22672280

22682281
if (ptr->object && Z_TYPE_P(ptr->object) == IS_OBJECT) {
2269-
if (ptr->function_state.function->common.scope) {
2270-
add_assoc_string_ex(stack_frame, "class", sizeof("class"), (char*)ptr->function_state.function->common.scope->name, 1);
2282+
if (fbc->common.scope) {
2283+
add_assoc_string_ex(stack_frame, "class", sizeof("class"), (char*) fbc->common.scope->name, 1);
22712284
} else {
22722285
zend_uint class_name_len;
22732286
int dup;
@@ -2282,8 +2295,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
22822295
}
22832296

22842297
add_assoc_string_ex(stack_frame, "type", sizeof("type"), "->", 1);
2285-
} else if (ptr->function_state.function->common.scope) {
2286-
add_assoc_string_ex(stack_frame, "class", sizeof("class"), (char*)ptr->function_state.function->common.scope->name, 1);
2298+
} else if (fbc->common.scope) {
2299+
add_assoc_string_ex(stack_frame, "class", sizeof("class"), (char*)fbc->common.scope->name, 1);
22872300
add_assoc_string_ex(stack_frame, "type", sizeof("type"), "::", 1);
22882301
}
22892302

Zend/zend_exceptions.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,18 @@ static int _build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list arg
502502
} else {
503503
TRACE_APPEND_STR("[internal function]: ");
504504
}
505+
505506
TRACE_APPEND_KEY("class");
506507
TRACE_APPEND_KEY("type");
507-
TRACE_APPEND_KEY("function");
508+
509+
if (zend_hash_exists(ht, "property", sizeof("property"))) {
510+
TRACE_APPEND_KEY("property");
511+
TRACE_APPEND_STR("->");
512+
TRACE_APPEND_KEY("accessor");
513+
} else {
514+
TRACE_APPEND_KEY("function");
515+
}
516+
508517
TRACE_APPEND_CHR('(');
509518
if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
510519
if (Z_TYPE_PP(tmp) == IS_ARRAY) {

Zend/zend_object_handlers.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,10 @@ zval *zend_std_read_property(zval *object, zval *member, int type, const zend_li
445445
guard->in_get = 1;
446446
zend_call_method_with_0_params(&object, zobj->ce, &getter, getter->common.function_name, &rv);
447447
guard->in_get = 0;
448-
if(rv) {
448+
if (rv) {
449449
Z_DELREF_P(rv);
450+
} else {
451+
rv = &EG(uninitialized_zval_ptr);
450452
}
451453
return rv;
452454
}

0 commit comments

Comments
 (0)