/src/php-src/Zend/zend_exceptions.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright © Zend Technologies Ltd., a subsidiary company of | |
6 | | | Perforce Software, Inc., and Contributors. | |
7 | | +----------------------------------------------------------------------+ |
8 | | | This source file is subject to the Modified BSD License that is | |
9 | | | bundled with this package in the file LICENSE, and is available | |
10 | | | through the World Wide Web at <https://www.php.net/license/>. | |
11 | | | | |
12 | | | SPDX-License-Identifier: BSD-3-Clause | |
13 | | +----------------------------------------------------------------------+ |
14 | | | Authors: Andi Gutmans <andi@php.net> | |
15 | | | Marcus Boerger <helly@php.net> | |
16 | | | Sterling Hughes <sterling@php.net> | |
17 | | | Zeev Suraski <zeev@php.net> | |
18 | | +----------------------------------------------------------------------+ |
19 | | */ |
20 | | |
21 | | #include "zend.h" |
22 | | #include "zend_API.h" |
23 | | #include "zend_builtin_functions.h" |
24 | | #include "zend_interfaces.h" |
25 | | #include "zend_exceptions.h" |
26 | | #include "zend_vm.h" |
27 | | #include "zend_dtrace.h" |
28 | | #include "zend_smart_str.h" |
29 | | #include "zend_exceptions_arginfo.h" |
30 | | #include "zend_observer.h" |
31 | | |
32 | 2.76k | #define ZEND_EXCEPTION_MESSAGE_OFF 0 |
33 | 77 | #define ZEND_EXCEPTION_CODE_OFF 2 |
34 | 952k | #define ZEND_EXCEPTION_FILE_OFF 3 |
35 | 952k | #define ZEND_EXCEPTION_LINE_OFF 4 |
36 | 952k | #define ZEND_EXCEPTION_TRACE_OFF 5 |
37 | 130 | #define ZEND_EXCEPTION_PREVIOUS_OFF 6 |
38 | | |
39 | | ZEND_API zend_class_entry *zend_ce_throwable; |
40 | | ZEND_API zend_class_entry *zend_ce_exception; |
41 | | ZEND_API zend_class_entry *zend_ce_error_exception; |
42 | | ZEND_API zend_class_entry *zend_ce_error; |
43 | | ZEND_API zend_class_entry *zend_ce_compile_error; |
44 | | ZEND_API zend_class_entry *zend_ce_parse_error; |
45 | | ZEND_API zend_class_entry *zend_ce_type_error; |
46 | | ZEND_API zend_class_entry *zend_ce_argument_count_error; |
47 | | ZEND_API zend_class_entry *zend_ce_value_error; |
48 | | ZEND_API zend_class_entry *zend_ce_arithmetic_error; |
49 | | ZEND_API zend_class_entry *zend_ce_division_by_zero_error; |
50 | | ZEND_API zend_class_entry *zend_ce_unhandled_match_error; |
51 | | ZEND_API zend_class_entry *zend_ce_request_parse_body_exception; |
52 | | |
53 | | /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */ |
54 | | static zend_class_entry zend_ce_unwind_exit; |
55 | | |
56 | | /* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does* execute finally blocks. */ |
57 | | static zend_class_entry zend_ce_graceful_exit; |
58 | | |
59 | | ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); |
60 | | |
61 | | static zend_object_handlers default_exception_handlers; |
62 | | |
63 | | /* {{{ zend_implement_throwable */ |
64 | | static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type) |
65 | 1.18k | { |
66 | | /* zend_ce_exception and zend_ce_error may not be initialized yet when this is called (e.g when |
67 | | * implementing Throwable for Exception itself). Perform a manual inheritance check. */ |
68 | 1.18k | const zend_class_entry *root = class_type; |
69 | 3.20k | while (root->parent) { |
70 | 2.01k | root = root->parent; |
71 | 2.01k | } |
72 | 1.18k | if (zend_string_equals_literal(root->name, "Exception") |
73 | 1.18k | || zend_string_equals_literal(root->name, "Error")) { |
74 | 1.18k | return SUCCESS; |
75 | 1.18k | } |
76 | | |
77 | 1.18k | bool can_extend = (class_type->ce_flags & ZEND_ACC_ENUM) == 0; |
78 | | |
79 | 8 | zend_error_noreturn(E_ERROR, |
80 | 8 | can_extend |
81 | 8 | ? "%s %s cannot implement interface %s, extend Exception or Error instead" |
82 | 8 | : "%s %s cannot implement interface %s", |
83 | 8 | zend_get_object_type_uc(class_type), |
84 | 8 | ZSTR_VAL(class_type->name), |
85 | 8 | ZSTR_VAL(interface->name)); |
86 | 0 | return FAILURE; |
87 | 1.18k | } |
88 | | /* }}} */ |
89 | | |
90 | | static inline zend_class_entry *i_get_exception_base(const zend_object *object) /* {{{ */ |
91 | 65.7M | { |
92 | 65.7M | return instanceof_function(object->ce, zend_ce_exception) ? zend_ce_exception : zend_ce_error; |
93 | 65.7M | } |
94 | | /* }}} */ |
95 | | |
96 | | ZEND_API zend_class_entry *zend_get_exception_base(const zend_object *object) /* {{{ */ |
97 | 0 | { |
98 | 0 | return i_get_exception_base(object); |
99 | 0 | } |
100 | | /* }}} */ |
101 | | |
102 | | void zend_exception_set_previous(zend_object *exception, zend_object *add_previous) /* {{{ */ |
103 | 806k | { |
104 | 806k | zval *previous, *ancestor, *ex; |
105 | 806k | zval pv, zv, rv; |
106 | 806k | zend_class_entry *base_ce; |
107 | | |
108 | 806k | if (!exception || !add_previous) { |
109 | 639k | return; |
110 | 639k | } |
111 | | |
112 | 167k | if (exception == add_previous || zend_is_unwind_exit(add_previous) || zend_is_graceful_exit(add_previous)) { |
113 | 113 | OBJ_RELEASE(add_previous); |
114 | 113 | return; |
115 | 113 | } |
116 | | |
117 | 167k | ZEND_ASSERT(instanceof_function(add_previous->ce, zend_ce_throwable) |
118 | 167k | && "Previous exception must implement Throwable"); |
119 | | |
120 | 167k | ZVAL_OBJ(&pv, add_previous); |
121 | 167k | ZVAL_OBJ(&zv, exception); |
122 | 167k | ex = &zv; |
123 | 170k | do { |
124 | 170k | ancestor = zend_read_property_ex(i_get_exception_base(add_previous), add_previous, ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
125 | 170k | ZVAL_DEREF(ancestor); |
126 | 61.5M | while (Z_TYPE_P(ancestor) == IS_OBJECT) { |
127 | 61.4M | if (Z_OBJ_P(ancestor) == Z_OBJ_P(ex)) { |
128 | 19 | OBJ_RELEASE(add_previous); |
129 | 19 | return; |
130 | 19 | } |
131 | 61.4M | ancestor = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(ancestor)), Z_OBJ_P(ancestor), ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
132 | 61.4M | ZVAL_DEREF(ancestor); |
133 | 61.4M | } |
134 | 170k | base_ce = i_get_exception_base(Z_OBJ_P(ex)); |
135 | 170k | previous = zend_read_property_ex(base_ce, Z_OBJ_P(ex), ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
136 | 170k | ZVAL_DEREF(previous); |
137 | 170k | if (Z_TYPE_P(previous) == IS_NULL) { |
138 | 167k | zend_update_property_ex(base_ce, Z_OBJ_P(ex), ZSTR_KNOWN(ZEND_STR_PREVIOUS), &pv); |
139 | 167k | GC_DELREF(add_previous); |
140 | 167k | return; |
141 | 167k | } |
142 | 3.71k | ex = previous; |
143 | 3.71k | } while (Z_OBJ_P(ex) != add_previous); |
144 | 167k | } |
145 | | /* }}} */ |
146 | | |
147 | 471k | static zend_always_inline bool is_handle_exception_set(void) { |
148 | 471k | zend_execute_data *execute_data = EG(current_execute_data); |
149 | 471k | return !execute_data |
150 | 471k | || !execute_data->func |
151 | 471k | || !ZEND_USER_CODE(execute_data->func->common.type) |
152 | 68.9k | || execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION; |
153 | 471k | } |
154 | | |
155 | | ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* {{{ */ |
156 | 806k | { |
157 | | #ifdef HAVE_DTRACE |
158 | | if (DTRACE_EXCEPTION_THROWN_ENABLED()) { |
159 | | if (exception != NULL) { |
160 | | DTRACE_EXCEPTION_THROWN(ZSTR_VAL(exception->ce->name)); |
161 | | } else { |
162 | | DTRACE_EXCEPTION_THROWN(NULL); |
163 | | } |
164 | | } |
165 | | #endif /* HAVE_DTRACE */ |
166 | | |
167 | 806k | if (exception != NULL) { |
168 | 805k | const zend_object *previous = EG(exception); |
169 | 805k | if (previous && zend_is_unwind_exit(previous)) { |
170 | | /* Don't replace unwinding exception with different exception. */ |
171 | 0 | OBJ_RELEASE(exception); |
172 | 0 | return; |
173 | 0 | } |
174 | | |
175 | 805k | zend_exception_set_previous(exception, EG(exception)); |
176 | 805k | EG(exception) = exception; |
177 | 805k | if (previous) { |
178 | 166k | return; |
179 | 166k | } |
180 | 805k | } |
181 | 640k | if (!EG(current_execute_data)) { |
182 | 168k | if (exception && (exception->ce == zend_ce_parse_error || exception->ce == zend_ce_compile_error)) { |
183 | 167k | return; |
184 | 167k | } |
185 | 1.03k | if (EG(exception)) { |
186 | 1.03k | if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF |
187 | 17 | && !zend_is_unwind_exit(EG(exception)) |
188 | 17 | && !zend_is_graceful_exit(EG(exception))) { |
189 | 17 | zend_user_exception_handler(); |
190 | 17 | if (EG(exception)) { |
191 | 0 | zend_exception_error(EG(exception), E_ERROR); |
192 | 0 | } |
193 | 17 | return; |
194 | 1.02k | } else { |
195 | 1.02k | zend_exception_error(EG(exception), E_ERROR); |
196 | 1.02k | } |
197 | 1.03k | zend_bailout(); |
198 | 1.03k | } |
199 | 0 | zend_error_noreturn(E_CORE_ERROR, "Exception thrown without a stack frame"); |
200 | 1.03k | } |
201 | | |
202 | 471k | if (zend_throw_exception_hook) { |
203 | 0 | zend_throw_exception_hook(exception); |
204 | 0 | } |
205 | | |
206 | 471k | if (is_handle_exception_set()) { |
207 | | /* no need to rethrow the exception */ |
208 | 402k | return; |
209 | 402k | } |
210 | 68.9k | EG(opline_before_exception) = EG(current_execute_data)->opline; |
211 | 68.9k | EG(current_execute_data)->opline = EG(exception_op); |
212 | 68.9k | } |
213 | | /* }}} */ |
214 | | |
215 | | ZEND_API void zend_clear_exception(void) /* {{{ */ |
216 | 169k | { |
217 | 169k | zend_object *exception; |
218 | 169k | if (!EG(exception)) { |
219 | 5.94k | return; |
220 | 5.94k | } |
221 | | /* exception may have destructor */ |
222 | 163k | exception = EG(exception); |
223 | 163k | EG(exception) = NULL; |
224 | 163k | OBJ_RELEASE(exception); |
225 | 163k | if (EG(current_execute_data)) { |
226 | 29.5k | EG(current_execute_data)->opline = EG(opline_before_exception); |
227 | 29.5k | } |
228 | 163k | #if ZEND_DEBUG |
229 | 163k | EG(opline_before_exception) = NULL; |
230 | 163k | #endif |
231 | 163k | } |
232 | | /* }}} */ |
233 | | |
234 | | /* Same as writing to OBJ_PROP_NUM() when there are no hooks, |
235 | | * but checks the offset is correct when Zend is built in debug mode. |
236 | | * This is faster than going through the regular property write routine when the offset is known at compile time. */ |
237 | | static void zend_update_property_num_checked(zend_class_entry *scope, zend_object *object, uint32_t prop_num, zend_string *member, zval *value) |
238 | 2.86M | { |
239 | 2.86M | if (UNEXPECTED(object->ce->num_hooked_props > 0)) { |
240 | | /* Property may have been overridden with a hook. */ |
241 | 209 | zend_update_property_ex(scope != NULL ? scope : object->ce, object, member, value); |
242 | 209 | zval_ptr_dtor(value); |
243 | 209 | return; |
244 | 209 | } |
245 | 2.86M | #if ZEND_DEBUG |
246 | 2.86M | const zend_class_entry *old_scope = EG(fake_scope); |
247 | 2.86M | EG(fake_scope) = i_get_exception_base(object); |
248 | 2.86M | const zend_property_info *prop_info = zend_get_property_info(object->ce, member, true); |
249 | 2.86M | ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_num); |
250 | 2.86M | EG(fake_scope) = old_scope; |
251 | 2.86M | #endif |
252 | 2.86M | zval *zv = OBJ_PROP_NUM(object, prop_num); |
253 | 2.86M | zval_ptr_safe_dtor(zv); |
254 | 2.86M | ZVAL_COPY_VALUE(zv, value); |
255 | 2.86M | } |
256 | | |
257 | | static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */ |
258 | 952k | { |
259 | 952k | zval tmp; |
260 | 952k | zval trace; |
261 | 952k | zend_string *filename; |
262 | | |
263 | 952k | zend_object *object = zend_objects_new(class_type); |
264 | 952k | object_properties_init(object, class_type); |
265 | | |
266 | 952k | if (EG(current_execute_data)) { |
267 | 785k | zend_fetch_debug_backtrace(&trace, |
268 | 785k | 0, |
269 | 785k | EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0); |
270 | 785k | } else { |
271 | 167k | ZVAL_EMPTY_ARRAY(&trace); |
272 | 167k | } |
273 | | |
274 | 952k | zend_update_property_num_checked(i_get_exception_base(object), object, ZEND_EXCEPTION_TRACE_OFF, ZSTR_KNOWN(ZEND_STR_TRACE), &trace); |
275 | | |
276 | 952k | if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error) |
277 | 952k | || !(filename = zend_get_compiled_filename()))) { |
278 | 730k | ZVAL_STRING(&tmp, zend_get_executed_filename()); |
279 | 730k | zend_update_property_num_checked(NULL, object, ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
280 | 730k | ZVAL_LONG(&tmp, zend_get_executed_lineno()); |
281 | 730k | zend_update_property_num_checked(NULL, object, ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
282 | 730k | } else { |
283 | 222k | ZVAL_STR_COPY(&tmp, filename); |
284 | 222k | zend_update_property_num_checked(NULL, object, ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
285 | 222k | ZVAL_LONG(&tmp, zend_get_compiled_lineno()); |
286 | 222k | zend_update_property_num_checked(NULL, object, ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
287 | 222k | } |
288 | | |
289 | 952k | return object; |
290 | 952k | } |
291 | | /* }}} */ |
292 | | |
293 | | /* {{{ Clone the exception object */ |
294 | | ZEND_COLD ZEND_METHOD(Exception, __clone) |
295 | 0 | { |
296 | | /* __clone() is private but this is reachable with reflection */ |
297 | 0 | zend_throw_exception(NULL, "Cannot clone object using __clone()", 0); |
298 | 0 | } |
299 | | /* }}} */ |
300 | | |
301 | | ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous) |
302 | 4.46k | { |
303 | 4.46k | zval tmp, *object = ZEND_THIS; |
304 | | |
305 | 4.46k | if (message) { |
306 | 2.76k | ZVAL_STR_COPY(&tmp, message); |
307 | 2.76k | zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); |
308 | 2.76k | if (UNEXPECTED(EG(exception))) { |
309 | 0 | return FAILURE; |
310 | 0 | } |
311 | 2.76k | } |
312 | | |
313 | 4.46k | if (code) { |
314 | 77 | ZVAL_LONG(&tmp, code); |
315 | 77 | zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); |
316 | 77 | if (UNEXPECTED(EG(exception))) { |
317 | 24 | return FAILURE; |
318 | 24 | } |
319 | 77 | } |
320 | | |
321 | 4.44k | if (previous) { |
322 | 130 | Z_ADDREF_P(previous); |
323 | 130 | zend_update_property_num_checked(zend_ce_exception, Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); |
324 | 130 | if (UNEXPECTED(EG(exception))) { |
325 | 0 | return FAILURE; |
326 | 0 | } |
327 | 130 | } |
328 | | |
329 | 4.44k | return SUCCESS; |
330 | 4.44k | } |
331 | | |
332 | | /* {{{ Exception constructor */ |
333 | | ZEND_METHOD(Exception, __construct) |
334 | 4.36k | { |
335 | 4.36k | zend_string *message = NULL; |
336 | 4.36k | zend_long code = 0; |
337 | 4.36k | zval *previous = NULL; |
338 | | |
339 | 4.36k | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { |
340 | 23 | RETURN_THROWS(); |
341 | 23 | } |
342 | | |
343 | 4.34k | if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { |
344 | 8 | RETURN_THROWS(); |
345 | 8 | } |
346 | 4.34k | } |
347 | | /* }}} */ |
348 | | |
349 | | /* {{{ Exception unserialize checks */ |
350 | | #define CHECK_EXC_TYPE(id, type) \ |
351 | 30.6k | pvalue = zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &value); \ |
352 | 30.6k | if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \ |
353 | 7 | zend_unset_property(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \ |
354 | 7 | } |
355 | | |
356 | | ZEND_METHOD(Exception, __wakeup) |
357 | 15.3k | { |
358 | 15.3k | ZEND_PARSE_PARAMETERS_NONE(); |
359 | | |
360 | 15.3k | zval value, *pvalue; |
361 | 15.3k | zval *object = ZEND_THIS; |
362 | 15.3k | CHECK_EXC_TYPE(ZEND_STR_MESSAGE, IS_STRING); |
363 | 15.3k | CHECK_EXC_TYPE(ZEND_STR_CODE, IS_LONG); |
364 | | /* The type of all other properties is enforced through typed properties. */ |
365 | 15.3k | } |
366 | | /* }}} */ |
367 | | |
368 | | /* {{{ ErrorException constructor */ |
369 | | ZEND_METHOD(ErrorException, __construct) |
370 | 126 | { |
371 | 126 | zend_string *message = NULL, *filename = NULL; |
372 | 126 | zend_long code = 0, severity = E_ERROR, lineno; |
373 | 126 | bool lineno_is_null = true; |
374 | 126 | zval tmp, *object, *previous = NULL; |
375 | | |
376 | 126 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SllS!l!O!", &message, &code, &severity, &filename, &lineno, &lineno_is_null, &previous, zend_ce_throwable) == FAILURE) { |
377 | 6 | RETURN_THROWS(); |
378 | 6 | } |
379 | | |
380 | 120 | object = ZEND_THIS; |
381 | | |
382 | 120 | if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { |
383 | 16 | RETURN_THROWS(); |
384 | 16 | } |
385 | | |
386 | 104 | ZVAL_LONG(&tmp, severity); |
387 | 104 | zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); |
388 | 104 | if (UNEXPECTED(EG(exception))) { |
389 | 0 | RETURN_THROWS(); |
390 | 0 | } |
391 | | |
392 | 104 | if (filename) { |
393 | 50 | ZVAL_STR_COPY(&tmp, filename); |
394 | 50 | zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
395 | 50 | if (UNEXPECTED(EG(exception))) { |
396 | 0 | RETURN_THROWS(); |
397 | 0 | } |
398 | 50 | } |
399 | | |
400 | 104 | if (!lineno_is_null) { |
401 | 35 | ZVAL_LONG(&tmp, lineno); |
402 | 35 | zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
403 | 35 | if (UNEXPECTED(EG(exception))) { |
404 | 0 | RETURN_THROWS(); |
405 | 0 | } |
406 | 69 | } else if (filename) { |
407 | 28 | ZVAL_LONG(&tmp, 0); |
408 | 28 | zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
409 | 28 | if (UNEXPECTED(EG(exception))) { |
410 | 0 | RETURN_THROWS(); |
411 | 0 | } |
412 | 28 | } |
413 | 104 | } |
414 | | /* }}} */ |
415 | | |
416 | | #define GET_PROPERTY(object, id) \ |
417 | 112k | zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 0, &rv) |
418 | | #define GET_PROPERTY_SILENT(object, id) \ |
419 | 3.20k | zend_read_property_ex(i_get_exception_base(Z_OBJ_P(object)), Z_OBJ_P(object), ZSTR_KNOWN(id), 1, &rv) |
420 | | |
421 | | /* {{{ Get the file in which the exception occurred */ |
422 | | ZEND_METHOD(Exception, getFile) |
423 | 166 | { |
424 | 166 | zval *prop, rv; |
425 | | |
426 | 166 | ZEND_PARSE_PARAMETERS_NONE(); |
427 | | |
428 | 164 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_FILE); |
429 | 164 | RETURN_STR(zval_get_string(prop)); |
430 | 164 | } |
431 | | /* }}} */ |
432 | | |
433 | | /* {{{ Get the line in which the exception occurred */ |
434 | | ZEND_METHOD(Exception, getLine) |
435 | 194 | { |
436 | 194 | zval *prop, rv; |
437 | | |
438 | 194 | ZEND_PARSE_PARAMETERS_NONE(); |
439 | | |
440 | 194 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_LINE); |
441 | 194 | RETURN_LONG(zval_get_long(prop)); |
442 | 194 | } |
443 | | /* }}} */ |
444 | | |
445 | | /* {{{ Get the exception message */ |
446 | | ZEND_METHOD(Exception, getMessage) |
447 | 77.0k | { |
448 | 77.0k | zval *prop, rv; |
449 | | |
450 | 77.0k | ZEND_PARSE_PARAMETERS_NONE(); |
451 | | |
452 | 77.0k | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_MESSAGE); |
453 | 77.0k | RETURN_STR(zval_get_string(prop)); |
454 | 77.0k | } |
455 | | /* }}} */ |
456 | | |
457 | | /* {{{ Get the exception code */ |
458 | | ZEND_METHOD(Exception, getCode) |
459 | 22 | { |
460 | 22 | zval *prop, rv; |
461 | | |
462 | 22 | ZEND_PARSE_PARAMETERS_NONE(); |
463 | | |
464 | 22 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_CODE); |
465 | 22 | ZVAL_DEREF(prop); |
466 | 22 | ZVAL_COPY(return_value, prop); |
467 | 22 | } |
468 | | /* }}} */ |
469 | | |
470 | | /* {{{ Get the stack trace for the location in which the exception occurred */ |
471 | | ZEND_METHOD(Exception, getTrace) |
472 | 136 | { |
473 | 136 | zval *prop, rv; |
474 | | |
475 | 136 | ZEND_PARSE_PARAMETERS_NONE(); |
476 | | |
477 | 136 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_TRACE); |
478 | 136 | ZVAL_DEREF(prop); |
479 | 136 | ZVAL_COPY(return_value, prop); |
480 | 136 | } |
481 | | /* }}} */ |
482 | | |
483 | | /* {{{ Get the exception severity */ |
484 | | ZEND_METHOD(ErrorException, getSeverity) |
485 | 7 | { |
486 | 7 | zval *prop, rv; |
487 | | |
488 | 7 | ZEND_PARSE_PARAMETERS_NONE(); |
489 | | |
490 | 7 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_SEVERITY); |
491 | 7 | ZVAL_DEREF(prop); |
492 | 7 | ZVAL_COPY(return_value, prop); |
493 | 7 | } |
494 | | /* }}} */ |
495 | | |
496 | 63.0k | #define TRACE_APPEND_KEY(key) do { \ |
497 | 63.0k | tmp = zend_hash_find(ht, key); \ |
498 | 63.0k | if (tmp) { \ |
499 | 35.9k | if (UNEXPECTED(Z_TYPE_P(tmp) != IS_STRING)) { \ |
500 | 15 | zend_error(E_WARNING, "Value for %s is not a string", \ |
501 | 15 | ZSTR_VAL(key)); \ |
502 | 15 | smart_str_appends(str, "[unknown]"); \ |
503 | 35.9k | } else { \ |
504 | 35.9k | smart_str_append(str, Z_STR_P(tmp)); \ |
505 | 35.9k | } \ |
506 | 35.9k | } \ |
507 | 63.0k | } while (0) |
508 | | |
509 | | static void build_trace_args(zval *arg, smart_str *str) /* {{{ */ |
510 | 33.9k | { |
511 | | /* the trivial way would be to do |
512 | | * convert_to_string(arg); |
513 | | * append it and kill the now tmp arg. |
514 | | * but that could cause some E_NOTICE and also damn long lines. |
515 | | */ |
516 | | |
517 | 33.9k | ZVAL_DEREF(arg); |
518 | | |
519 | 33.9k | if (smart_str_append_zval(str, arg, EG(exception_string_param_max_len)) != SUCCESS) { |
520 | 1.57k | switch (Z_TYPE_P(arg)) { |
521 | 0 | case IS_RESOURCE: |
522 | 0 | smart_str_appends(str, "Resource id #"); |
523 | 0 | smart_str_append_long(str, Z_RES_HANDLE_P(arg)); |
524 | 0 | break; |
525 | 691 | case IS_ARRAY: |
526 | 691 | smart_str_appends(str, "Array"); |
527 | 691 | break; |
528 | 882 | case IS_OBJECT: { |
529 | 882 | zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg)); |
530 | 882 | smart_str_appends(str, "Object("); |
531 | | /* cut off on NULL byte ... class@anonymous */ |
532 | 882 | smart_str_appends(str, ZSTR_VAL(class_name)); |
533 | 882 | smart_str_appends(str, ")"); |
534 | 882 | zend_string_release_ex(class_name, 0); |
535 | 882 | break; |
536 | 0 | } |
537 | 1.57k | } |
538 | 1.57k | } |
539 | 33.9k | } |
540 | | /* }}} */ |
541 | | |
542 | | static void build_trace_args_list(zval *tmp, smart_str *str) /* {{{ */ |
543 | 31.3k | { |
544 | 31.3k | if (UNEXPECTED(Z_TYPE_P(tmp) != IS_ARRAY)) { |
545 | | /* only happens w/ reflection abuse (Zend/tests/bug63762.phpt) */ |
546 | 10 | zend_error(E_WARNING, "args element is not an array"); |
547 | 10 | return; |
548 | 10 | } |
549 | | |
550 | 31.3k | bool first = true; |
551 | 99.1k | ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), zend_string *name, zval *arg) { |
552 | 99.1k | if (!first) { |
553 | 4.09k | smart_str_appends(str, ", "); |
554 | 4.09k | } |
555 | 99.1k | first = false; |
556 | 99.1k | if (name) { |
557 | 51 | smart_str_append(str, name); |
558 | 51 | smart_str_appends(str, ": "); |
559 | 51 | } |
560 | 99.1k | build_trace_args(arg, str); |
561 | 99.1k | } ZEND_HASH_FOREACH_END(); |
562 | 31.3k | } |
563 | | /* }}} */ |
564 | | |
565 | | static void build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */ |
566 | 31.5k | { |
567 | 31.5k | zval *file, *tmp; |
568 | | |
569 | 31.5k | smart_str_appendc(str, '#'); |
570 | 31.5k | smart_str_append_long(str, num); |
571 | 31.5k | smart_str_appendc(str, ' '); |
572 | | |
573 | 31.5k | file = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_FILE)); |
574 | 31.5k | if (file) { |
575 | 30.2k | if (UNEXPECTED(Z_TYPE_P(file) != IS_STRING)) { |
576 | 10 | zend_error(E_WARNING, "File name is not a string"); |
577 | 10 | smart_str_appends(str, "[unknown file]: "); |
578 | 30.2k | } else{ |
579 | 30.2k | zend_long line = 0; |
580 | 30.2k | tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_LINE)); |
581 | 30.2k | if (tmp) { |
582 | 30.2k | if (EXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { |
583 | 30.2k | line = Z_LVAL_P(tmp); |
584 | 30.2k | } else { |
585 | 0 | zend_error(E_WARNING, "Line is not an int"); |
586 | 0 | } |
587 | 30.2k | } |
588 | 30.2k | smart_str_append(str, Z_STR_P(file)); |
589 | 30.2k | smart_str_appendc(str, '('); |
590 | 30.2k | smart_str_append_long(str, line); |
591 | 30.2k | smart_str_appends(str, "): "); |
592 | 30.2k | } |
593 | 30.2k | } else { |
594 | 1.29k | smart_str_appends(str, "[internal function]: "); |
595 | 1.29k | } |
596 | 31.5k | const zval *class_name = zend_hash_find(ht, ZSTR_KNOWN(ZEND_STR_CLASS)); |
597 | 31.5k | if (class_name) { |
598 | 4.42k | if (UNEXPECTED(Z_TYPE_P(class_name) != IS_STRING)) { |
599 | 10 | zend_error(E_WARNING, "Value for class is not a string"); |
600 | 10 | smart_str_appends(str, "[unknown]"); |
601 | 4.41k | } else { |
602 | | /* cut off on NULL byte ... class@anonymous */ |
603 | 4.41k | smart_str_appends(str, Z_STRVAL_P(class_name)); |
604 | 4.41k | } |
605 | 4.42k | } |
606 | 31.5k | TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE)); |
607 | 31.5k | TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION)); |
608 | 31.5k | smart_str_appendc(str, '('); |
609 | 31.5k | tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_ARGS)); |
610 | 31.5k | if (tmp) { |
611 | 31.3k | build_trace_args_list(tmp, str); |
612 | 31.3k | } |
613 | 31.5k | smart_str_appends(str, ")\n"); |
614 | 31.5k | } |
615 | | /* }}} */ |
616 | | |
617 | | /* {{{ Gets the function arguments printed as a string from a backtrace frame. */ |
618 | 0 | ZEND_API zend_string *zend_trace_function_args_to_string(const HashTable *frame) { |
619 | 0 | smart_str str = {0}; |
620 | |
|
621 | 0 | zval *tmp = zend_hash_find_known_hash(frame, ZSTR_KNOWN(ZEND_STR_ARGS)); |
622 | 0 | if (tmp) { |
623 | 0 | build_trace_args_list(tmp, &str); |
624 | 0 | } |
625 | |
|
626 | 0 | return smart_str_extract(&str); |
627 | 0 | } |
628 | | /* }}} */ |
629 | | |
630 | | /* {{{ Gets the currently executing function's arguments as a string. Used by php_verror. */ |
631 | 0 | ZEND_API zend_string *zend_trace_current_function_args_string(void) { |
632 | 0 | zend_string *dynamic_params = NULL; |
633 | | /* Special case: require_once/include_once aren't functions, but we |
634 | | * want to capture their arguments anyways, for i.e. file not found. |
635 | | */ |
636 | 0 | zend_execute_data *execute_data = EG(current_execute_data); |
637 | 0 | if (execute_data && execute_data->func |
638 | 0 | && ZEND_USER_CODE(execute_data->func->common.type) |
639 | 0 | && (execute_data->opline->opcode == ZEND_INCLUDE_OR_EVAL)) { |
640 | 0 | zval *inc_filename; |
641 | |
|
642 | 0 | switch (execute_data->opline->op1_type) { |
643 | | /* op1 may be CONST, TMP or CV; RT_CONSTANT() is only valid for the former. */ |
644 | 0 | case IS_CONST: |
645 | 0 | inc_filename = RT_CONSTANT(execute_data->opline, execute_data->opline->op1); |
646 | 0 | break; |
647 | 0 | default: |
648 | 0 | inc_filename = EX_VAR(execute_data->opline->op1.var); |
649 | 0 | } |
650 | | |
651 | 0 | smart_str str = {0}; |
652 | 0 | build_trace_args(inc_filename, &str); |
653 | 0 | return smart_str_extract(&str); |
654 | 0 | } |
655 | | |
656 | | /* get a backtrace to snarf function args */ |
657 | 0 | zval backtrace; |
658 | 0 | zend_fetch_debug_backtrace(&backtrace, /* skip_last */ 0, /* options */ 0, /* limit */ 1); |
659 | | /* can fail esp if low memory condition */ |
660 | 0 | if (Z_TYPE(backtrace) == IS_ARRAY) { |
661 | 0 | zval *first_frame = zend_hash_index_find(Z_ARRVAL(backtrace), 0); |
662 | 0 | if (first_frame) { |
663 | 0 | dynamic_params = zend_trace_function_args_to_string(Z_ARRVAL_P(first_frame)); |
664 | 0 | } |
665 | 0 | } |
666 | 0 | zval_ptr_dtor(&backtrace); |
667 | 0 | return dynamic_params; |
668 | 0 | } |
669 | | /* }}} */ |
670 | | |
671 | 13.1k | ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_main) { |
672 | 13.1k | zend_ulong index; |
673 | 13.1k | zval *frame; |
674 | 13.1k | uint32_t num = 0; |
675 | 13.1k | smart_str str = {0}; |
676 | | |
677 | 76.1k | ZEND_HASH_FOREACH_NUM_KEY_VAL(trace, index, frame) { |
678 | 76.1k | if (UNEXPECTED(Z_TYPE_P(frame) != IS_ARRAY)) { |
679 | 20 | zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index); |
680 | 20 | continue; |
681 | 20 | } |
682 | | |
683 | 31.5k | build_trace_string(&str, Z_ARRVAL_P(frame), num++); |
684 | 31.5k | } ZEND_HASH_FOREACH_END(); |
685 | | |
686 | 13.1k | if (include_main) { |
687 | 12.7k | smart_str_appendc(&str, '#'); |
688 | 12.7k | smart_str_append_long(&str, num); |
689 | 12.7k | smart_str_appends(&str, " {main}"); |
690 | 12.7k | } |
691 | | |
692 | 13.1k | smart_str_0(&str); |
693 | 13.1k | return str.s ? str.s : ZSTR_EMPTY_ALLOC(); |
694 | 13.1k | } |
695 | | |
696 | | /* {{{ Obtain the backtrace for the exception as a string (instead of an array) */ |
697 | | ZEND_METHOD(Exception, getTraceAsString) |
698 | 5.43k | { |
699 | | |
700 | 5.43k | ZEND_PARSE_PARAMETERS_NONE(); |
701 | | |
702 | 5.43k | zval *object = ZEND_THIS; |
703 | 5.43k | zend_class_entry *base_ce = i_get_exception_base(Z_OBJ_P(object)); |
704 | 5.43k | zval rv; |
705 | 5.43k | const zval *trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv); |
706 | 5.43k | if (EG(exception)) { |
707 | 0 | RETURN_THROWS(); |
708 | 0 | } |
709 | | |
710 | 5.43k | ZVAL_DEREF(trace); |
711 | | /* Type should be guaranteed by property type. */ |
712 | 5.43k | ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY); |
713 | 5.43k | RETURN_NEW_STR(zend_trace_to_string(Z_ARRVAL_P(trace), /* include_main */ true)); |
714 | 5.43k | } |
715 | | /* }}} */ |
716 | | |
717 | | /* {{{ Return previous Throwable or NULL. */ |
718 | | ZEND_METHOD(Exception, getPrevious) |
719 | 419 | { |
720 | 419 | zval rv; |
721 | | |
722 | 419 | ZEND_PARSE_PARAMETERS_NONE(); |
723 | | |
724 | 419 | ZVAL_COPY_DEREF(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_PREVIOUS)); |
725 | 419 | } /* }}} */ |
726 | | |
727 | | /* {{{ Obtain the string representation of the Exception object */ |
728 | | ZEND_METHOD(Exception, __toString) |
729 | 7.09k | { |
730 | 7.09k | zval trace, *exception; |
731 | 7.09k | zend_class_entry *base_ce; |
732 | 7.09k | zend_string *str; |
733 | 7.09k | zval rv, tmp; |
734 | | |
735 | 7.09k | ZEND_PARSE_PARAMETERS_NONE(); |
736 | | |
737 | 7.09k | str = ZSTR_EMPTY_ALLOC(); |
738 | | |
739 | 7.09k | exception = ZEND_THIS; |
740 | 7.09k | base_ce = i_get_exception_base(Z_OBJ_P(exception)); |
741 | | |
742 | | /* As getTraceAsString method is final we can grab it once */ |
743 | 7.09k | zend_function *getTraceAsString = zend_hash_str_find_ptr(&base_ce->function_table, ZEND_STRL("gettraceasstring")); |
744 | 7.09k | ZEND_ASSERT(getTraceAsString && "Method getTraceAsString must exist"); |
745 | | |
746 | | |
747 | 7.09k | zend_fcall_info fci; |
748 | 7.09k | fci.size = sizeof(fci); |
749 | 7.09k | fci.retval = &trace; |
750 | 7.09k | fci.param_count = 0; |
751 | 7.09k | fci.params = NULL; |
752 | 7.09k | fci.named_params = NULL; |
753 | | |
754 | 7.09k | zend_fcall_info_cache fcc; |
755 | 7.09k | fcc.function_handler = getTraceAsString; |
756 | 7.09k | fcc.called_scope = base_ce; |
757 | 7.09k | fcc.closure = NULL; |
758 | | |
759 | 14.4k | while (exception && Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) { |
760 | 7.33k | zend_string *prev_str = str; |
761 | 7.33k | zend_string *message = zval_get_string(GET_PROPERTY(exception, ZEND_STR_MESSAGE)); |
762 | 7.33k | zend_string *file = zval_get_string(GET_PROPERTY(exception, ZEND_STR_FILE)); |
763 | 7.33k | zend_long line = zval_get_long(GET_PROPERTY(exception, ZEND_STR_LINE)); |
764 | | |
765 | 7.33k | fcc.object = Z_OBJ_P(exception); |
766 | 7.33k | zend_call_function(&fci, &fcc); |
767 | | |
768 | 7.33k | if (Z_TYPE(trace) != IS_STRING) { |
769 | 0 | zval_ptr_dtor(&trace); |
770 | 0 | ZVAL_UNDEF(&trace); |
771 | 0 | } |
772 | | |
773 | 7.33k | if ((Z_OBJCE_P(exception) == zend_ce_type_error || Z_OBJCE_P(exception) == zend_ce_argument_count_error) && strstr(ZSTR_VAL(message), ", called in ")) { |
774 | 59 | zend_string *real_message = zend_strpprintf_unchecked(0, "%S and defined", message); |
775 | 59 | zend_string_release_ex(message, 0); |
776 | 59 | message = real_message; |
777 | 59 | } |
778 | | |
779 | 7.33k | zend_string *tmp_trace = (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) |
780 | 7.33k | ? zend_string_copy(Z_STR(trace)) |
781 | 7.33k | : ZSTR_INIT_LITERAL("#0 {main}\n", false); |
782 | | |
783 | 7.33k | zend_string *name = Z_OBJCE_P(exception)->name; |
784 | | |
785 | 7.33k | if (ZSTR_LEN(message) > 0) { |
786 | 4.67k | zval message_zv; |
787 | 4.67k | ZVAL_STR(&message_zv, message); |
788 | | |
789 | 4.67k | str = zend_strpprintf_unchecked(0, "%S: %S in %S:" ZEND_LONG_FMT "\nStack trace:\n%S%s%S", |
790 | 4.67k | name, message, file, line, |
791 | 4.67k | tmp_trace, ZSTR_LEN(prev_str) ? "\n\nNext " : "", prev_str); |
792 | 4.67k | } else { |
793 | 2.66k | str = zend_strpprintf_unchecked(0, "%S in %S:" ZEND_LONG_FMT "\nStack trace:\n%S%s%S", |
794 | 2.66k | name, file, line, |
795 | 2.66k | tmp_trace, ZSTR_LEN(prev_str) ? "\n\nNext " : "", prev_str); |
796 | 2.66k | } |
797 | 7.33k | zend_string_release_ex(tmp_trace, false); |
798 | | |
799 | 7.33k | zend_string_release_ex(prev_str, 0); |
800 | 7.33k | zend_string_release_ex(message, 0); |
801 | 7.33k | zend_string_release_ex(file, 0); |
802 | 7.33k | zval_ptr_dtor(&trace); |
803 | | |
804 | 7.33k | Z_PROTECT_RECURSION_P(exception); |
805 | 7.33k | exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS); |
806 | 7.33k | ZVAL_DEREF(exception); |
807 | 7.33k | if (Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) { |
808 | 0 | break; |
809 | 0 | } |
810 | 7.33k | } |
811 | | |
812 | 7.09k | exception = ZEND_THIS; |
813 | | /* Reset apply counts */ |
814 | 7.09k | zend_class_entry *previous_base_ce; |
815 | 12.4k | while (Z_TYPE_P(exception) == IS_OBJECT && (previous_base_ce = i_get_exception_base(Z_OBJ_P(exception))) && instanceof_function(Z_OBJCE_P(exception), previous_base_ce)) { |
816 | 5.35k | if (Z_IS_RECURSIVE_P(exception)) { |
817 | 5.35k | Z_UNPROTECT_RECURSION_P(exception); |
818 | 5.35k | } else { |
819 | 0 | break; |
820 | 0 | } |
821 | 5.35k | exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS); |
822 | 5.35k | ZVAL_DEREF(exception); |
823 | 5.35k | } |
824 | | |
825 | | /* We store the result in the private property string so we can access |
826 | | * the result in uncaught exception handlers without memleaks. */ |
827 | 7.09k | ZVAL_STR(&tmp, str); |
828 | 7.09k | zend_update_property_ex(base_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_STRING), &tmp); |
829 | | |
830 | 7.09k | RETURN_STR(str); |
831 | 7.09k | } |
832 | | /* }}} */ |
833 | | |
834 | 192 | static void zend_init_exception_class_entry(zend_class_entry *ce) { |
835 | 192 | ce->create_object = zend_default_exception_new; |
836 | 192 | ce->default_object_handlers = &default_exception_handlers; |
837 | 192 | } |
838 | | |
839 | | void zend_register_default_exception(void) /* {{{ */ |
840 | 16 | { |
841 | 16 | zend_ce_throwable = register_class_Throwable(zend_ce_stringable); |
842 | 16 | zend_ce_throwable->interface_gets_implemented = zend_implement_throwable; |
843 | | |
844 | 16 | memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers)); |
845 | 16 | default_exception_handlers.clone_obj = NULL; |
846 | | |
847 | 16 | zend_ce_exception = register_class_Exception(zend_ce_throwable); |
848 | 16 | zend_init_exception_class_entry(zend_ce_exception); |
849 | | |
850 | 16 | zend_ce_error_exception = register_class_ErrorException(zend_ce_exception); |
851 | 16 | zend_init_exception_class_entry(zend_ce_error_exception); |
852 | | |
853 | 16 | zend_ce_error = register_class_Error(zend_ce_throwable); |
854 | 16 | zend_init_exception_class_entry(zend_ce_error); |
855 | | |
856 | 16 | zend_ce_compile_error = register_class_CompileError(zend_ce_error); |
857 | 16 | zend_init_exception_class_entry(zend_ce_compile_error); |
858 | | |
859 | 16 | zend_ce_parse_error = register_class_ParseError(zend_ce_compile_error); |
860 | 16 | zend_init_exception_class_entry(zend_ce_parse_error); |
861 | | |
862 | 16 | zend_ce_type_error = register_class_TypeError(zend_ce_error); |
863 | 16 | zend_init_exception_class_entry(zend_ce_type_error); |
864 | | |
865 | 16 | zend_ce_argument_count_error = register_class_ArgumentCountError(zend_ce_type_error); |
866 | 16 | zend_init_exception_class_entry(zend_ce_argument_count_error); |
867 | | |
868 | 16 | zend_ce_value_error = register_class_ValueError(zend_ce_error); |
869 | 16 | zend_init_exception_class_entry(zend_ce_value_error); |
870 | | |
871 | 16 | zend_ce_arithmetic_error = register_class_ArithmeticError(zend_ce_error); |
872 | 16 | zend_init_exception_class_entry(zend_ce_arithmetic_error); |
873 | | |
874 | 16 | zend_ce_division_by_zero_error = register_class_DivisionByZeroError(zend_ce_arithmetic_error); |
875 | 16 | zend_init_exception_class_entry(zend_ce_division_by_zero_error); |
876 | | |
877 | 16 | zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error); |
878 | 16 | zend_init_exception_class_entry(zend_ce_unhandled_match_error); |
879 | | |
880 | 16 | zend_ce_request_parse_body_exception = register_class_RequestParseBodyException(zend_ce_exception); |
881 | 16 | zend_init_exception_class_entry(zend_ce_request_parse_body_exception); |
882 | | |
883 | 16 | INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); |
884 | | |
885 | 16 | INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL); |
886 | 16 | } |
887 | | /* }}} */ |
888 | | |
889 | | static zend_object *zend_throw_exception_zstr(zend_class_entry *exception_ce, zend_string *message, zend_long code) /* {{{ */ |
890 | 801k | { |
891 | 801k | zval ex, tmp; |
892 | | |
893 | 801k | if (!exception_ce) { |
894 | 495 | exception_ce = zend_ce_exception; |
895 | 495 | } |
896 | | |
897 | 801k | ZEND_ASSERT(instanceof_function(exception_ce, zend_ce_throwable) |
898 | 801k | && "Exceptions must implement Throwable"); |
899 | | |
900 | 801k | object_init_ex(&ex, exception_ce); |
901 | | |
902 | 801k | if (message) { |
903 | 801k | ZVAL_STR(&tmp, message); |
904 | 801k | zend_update_property_ex(exception_ce, Z_OBJ(ex), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); |
905 | 801k | } |
906 | 801k | if (code) { |
907 | 835 | ZVAL_LONG(&tmp, code); |
908 | 835 | zend_update_property_ex(exception_ce, Z_OBJ(ex), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); |
909 | 835 | } |
910 | | |
911 | 801k | zend_throw_exception_internal(Z_OBJ(ex)); |
912 | | |
913 | 801k | return Z_OBJ(ex); |
914 | 801k | } |
915 | | /* }}} */ |
916 | | |
917 | | ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */ |
918 | 334k | { |
919 | 334k | zend_string *msg_str = message ? zend_string_init(message, strlen(message), 0) : NULL; |
920 | 334k | zend_object *ex = zend_throw_exception_zstr(exception_ce, msg_str, code); |
921 | 334k | if (msg_str) { |
922 | 334k | zend_string_release(msg_str); |
923 | 334k | } |
924 | 334k | return ex; |
925 | 334k | } |
926 | | /* }}} */ |
927 | | |
928 | | ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format, ...) /* {{{ */ |
929 | 466k | { |
930 | 466k | va_list arg; |
931 | 466k | zend_object *obj; |
932 | | |
933 | 466k | va_start(arg, format); |
934 | 466k | zend_string *msg_str = zend_vstrpprintf(0, format, arg); |
935 | 466k | va_end(arg); |
936 | 466k | obj = zend_throw_exception_zstr(exception_ce, msg_str, code); |
937 | 466k | zend_string_release(msg_str); |
938 | 466k | return obj; |
939 | 466k | } |
940 | | /* }}} */ |
941 | | |
942 | | ZEND_API ZEND_COLD zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, zend_string *message, zend_long code, int severity) /* {{{ */ |
943 | 0 | { |
944 | 0 | zend_object *obj = zend_throw_exception_zstr(exception_ce, message, code); |
945 | 0 | if (exception_ce && instanceof_function(exception_ce, zend_ce_error_exception)) { |
946 | 0 | zval tmp; |
947 | 0 | ZVAL_LONG(&tmp, severity); |
948 | 0 | zend_update_property_ex(zend_ce_error_exception, obj, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); |
949 | 0 | } |
950 | 0 | return obj; |
951 | 0 | } |
952 | | /* }}} */ |
953 | | |
954 | | static void zend_error_va(int type, zend_string *file, uint32_t lineno, const char *format, ...) /* {{{ */ |
955 | 1.06k | { |
956 | 1.06k | va_list args; |
957 | 1.06k | va_start(args, format); |
958 | 1.06k | zend_string *message = zend_vstrpprintf(0, format, args); |
959 | 1.06k | zend_observer_error_notify(type, file, lineno, message); |
960 | 1.06k | zend_error_cb(type, file, lineno, message); |
961 | 1.06k | zend_string_release(message); |
962 | 1.06k | va_end(args); |
963 | 1.06k | } |
964 | | /* }}} */ |
965 | | |
966 | | /* This function doesn't return if it uses E_ERROR */ |
967 | | ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severity) /* {{{ */ |
968 | 1.08k | { |
969 | 1.08k | zval exception, rv; |
970 | 1.08k | zend_class_entry *ce_exception; |
971 | 1.08k | zend_result result = FAILURE; |
972 | | |
973 | 1.08k | ZVAL_OBJ(&exception, ex); |
974 | 1.08k | ce_exception = ex->ce; |
975 | 1.08k | EG(exception) = NULL; |
976 | | |
977 | 1.08k | zval_ptr_dtor(&EG(last_fatal_error_backtrace)); |
978 | 1.08k | ZVAL_UNDEF(&EG(last_fatal_error_backtrace)); |
979 | | |
980 | 1.08k | if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) { |
981 | 6 | zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE)); |
982 | 6 | zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE)); |
983 | 6 | zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE)); |
984 | 6 | int type = (ce_exception == zend_ce_parse_error ? E_PARSE : E_COMPILE_ERROR) | E_DONT_BAIL; |
985 | | |
986 | 6 | zend_observer_error_notify(type, file, line, message); |
987 | 6 | zend_error_cb(type, file, line, message); |
988 | | |
989 | 6 | zend_string_release_ex(file, 0); |
990 | 6 | zend_string_release_ex(message, 0); |
991 | 1.07k | } else if (instanceof_function(ce_exception, zend_ce_throwable)) { |
992 | 1.06k | zval tmp; |
993 | 1.06k | zend_string *str, *file = NULL; |
994 | 1.06k | zend_long line = 0; |
995 | | |
996 | 1.06k | zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); |
997 | 1.06k | if (!EG(exception)) { |
998 | 1.06k | if (UNEXPECTED(Z_ISREF(tmp))) { |
999 | 0 | zend_unwrap_reference(&tmp); |
1000 | 0 | } |
1001 | 1.06k | if (Z_TYPE(tmp) != IS_STRING) { |
1002 | 0 | zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name)); |
1003 | 1.06k | } else { |
1004 | 1.06k | zend_update_property_ex(i_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), &tmp); |
1005 | 1.06k | } |
1006 | 1.06k | } |
1007 | 1.06k | zval_ptr_dtor(&tmp); |
1008 | | |
1009 | 1.06k | if (EG(exception)) { |
1010 | 0 | zval zv; |
1011 | |
|
1012 | 0 | ZVAL_OBJ(&zv, EG(exception)); |
1013 | | /* do the best we can to inform about the inner exception */ |
1014 | 0 | if (instanceof_function(ce_exception, zend_ce_exception) || instanceof_function(ce_exception, zend_ce_error)) { |
1015 | 0 | file = zval_get_string(GET_PROPERTY_SILENT(&zv, ZEND_STR_FILE)); |
1016 | 0 | line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE)); |
1017 | 0 | } |
1018 | |
|
1019 | 0 | zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? file : NULL, line, |
1020 | 0 | "Uncaught %s in exception handling during call to %s::__toString()", |
1021 | 0 | ZSTR_VAL(Z_OBJCE(zv)->name), ZSTR_VAL(ce_exception->name)); |
1022 | |
|
1023 | 0 | if (file) { |
1024 | 0 | zend_string_release_ex(file, 0); |
1025 | 0 | } |
1026 | 0 | } |
1027 | | |
1028 | 1.06k | str = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_STRING)); |
1029 | 1.06k | file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE)); |
1030 | 1.06k | line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE)); |
1031 | | |
1032 | 1.06k | zend_error_va(severity | E_DONT_BAIL, |
1033 | 1.06k | (file && ZSTR_LEN(file) > 0) ? file : NULL, line, |
1034 | 1.06k | "Uncaught %S\n thrown", str); |
1035 | | |
1036 | 1.06k | zend_string_release_ex(str, 0); |
1037 | 1.06k | zend_string_release_ex(file, 0); |
1038 | 1.06k | } else if (ce_exception == &zend_ce_unwind_exit || ce_exception == &zend_ce_graceful_exit) { |
1039 | | /* We successfully unwound, nothing more to do. |
1040 | | * We still return FAILURE in this case, as further execution should still be aborted. */ |
1041 | 15 | } else { |
1042 | 0 | zend_error(severity, "Uncaught exception %s", ZSTR_VAL(ce_exception->name)); |
1043 | 0 | } |
1044 | | |
1045 | 1.08k | OBJ_RELEASE(ex); |
1046 | 1.08k | return result; |
1047 | 1.08k | } |
1048 | | /* }}} */ |
1049 | | |
1050 | 25 | ZEND_NORETURN void zend_exception_uncaught_error(const char *format, ...) { |
1051 | 25 | va_list va; |
1052 | 25 | va_start(va, format); |
1053 | 25 | zend_string *prefix = zend_vstrpprintf(0, format, va); |
1054 | 25 | va_end(va); |
1055 | | |
1056 | 25 | ZEND_ASSERT(EG(exception)); |
1057 | 25 | zval exception_zv; |
1058 | 25 | ZVAL_OBJ_COPY(&exception_zv, EG(exception)); |
1059 | 25 | zend_clear_exception(); |
1060 | | |
1061 | 25 | zend_string *exception_str = zval_get_string(&exception_zv); |
1062 | 25 | zend_error_noreturn(E_ERROR, |
1063 | 25 | "%s: Uncaught %s", ZSTR_VAL(prefix), ZSTR_VAL(exception_str)); |
1064 | 25 | } |
1065 | | |
1066 | | ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */ |
1067 | 3.38k | { |
1068 | 3.38k | if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) { |
1069 | 0 | zend_error_noreturn(E_CORE_ERROR, "Need to supply an object when throwing an exception"); |
1070 | 0 | } |
1071 | | |
1072 | 3.38k | zend_class_entry *exception_ce = Z_OBJCE_P(exception); |
1073 | | |
1074 | 3.38k | if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) { |
1075 | 4 | zend_throw_error(NULL, "Cannot throw objects that do not implement Throwable"); |
1076 | 4 | zval_ptr_dtor(exception); |
1077 | 4 | return; |
1078 | 4 | } |
1079 | | |
1080 | 3.37k | zend_throw_exception_internal(Z_OBJ_P(exception)); |
1081 | 3.37k | } |
1082 | | /* }}} */ |
1083 | | |
1084 | | ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void) |
1085 | 182 | { |
1086 | 182 | return zend_objects_new(&zend_ce_unwind_exit); |
1087 | 182 | } |
1088 | | |
1089 | | ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void) |
1090 | 727 | { |
1091 | 727 | return zend_objects_new(&zend_ce_graceful_exit); |
1092 | 727 | } |
1093 | | |
1094 | | ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) |
1095 | 182 | { |
1096 | 182 | ZEND_ASSERT(!EG(exception)); |
1097 | 182 | EG(exception) = zend_create_unwind_exit(); |
1098 | 182 | EG(opline_before_exception) = EG(current_execute_data)->opline; |
1099 | 182 | EG(current_execute_data)->opline = EG(exception_op); |
1100 | 182 | } |
1101 | | |
1102 | | ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) |
1103 | 0 | { |
1104 | 0 | ZEND_ASSERT(!EG(exception)); |
1105 | 0 | EG(exception) = zend_create_graceful_exit(); |
1106 | 0 | EG(opline_before_exception) = EG(current_execute_data)->opline; |
1107 | 0 | EG(current_execute_data)->opline = EG(exception_op); |
1108 | 0 | } |
1109 | | |
1110 | | ZEND_API bool zend_is_unwind_exit(const zend_object *ex) |
1111 | 335k | { |
1112 | 335k | return ex->ce == &zend_ce_unwind_exit; |
1113 | 335k | } |
1114 | | |
1115 | | ZEND_API bool zend_is_graceful_exit(const zend_object *ex) |
1116 | 169k | { |
1117 | 169k | return ex->ce == &zend_ce_graceful_exit; |
1118 | 169k | } |