/src/php-src/Zend/zend_exceptions.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 2.00 of the Zend license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | http://www.zend.com/license/2_00.txt. | |
11 | | | If you did not receive a copy of the Zend license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@zend.com so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Andi Gutmans <andi@php.net> | |
16 | | | Marcus Boerger <helly@php.net> | |
17 | | | Sterling Hughes <sterling@php.net> | |
18 | | | Zeev Suraski <zeev@php.net> | |
19 | | +----------------------------------------------------------------------+ |
20 | | */ |
21 | | |
22 | | #include "zend.h" |
23 | | #include "zend_API.h" |
24 | | #include "zend_builtin_functions.h" |
25 | | #include "zend_interfaces.h" |
26 | | #include "zend_exceptions.h" |
27 | | #include "zend_vm.h" |
28 | | #include "zend_dtrace.h" |
29 | | #include "zend_smart_str.h" |
30 | | #include "zend_exceptions_arginfo.h" |
31 | | |
32 | | ZEND_API zend_class_entry *zend_ce_throwable; |
33 | | ZEND_API zend_class_entry *zend_ce_exception; |
34 | | ZEND_API zend_class_entry *zend_ce_error_exception; |
35 | | ZEND_API zend_class_entry *zend_ce_error; |
36 | | ZEND_API zend_class_entry *zend_ce_compile_error; |
37 | | ZEND_API zend_class_entry *zend_ce_parse_error; |
38 | | ZEND_API zend_class_entry *zend_ce_type_error; |
39 | | ZEND_API zend_class_entry *zend_ce_argument_count_error; |
40 | | ZEND_API zend_class_entry *zend_ce_value_error; |
41 | | ZEND_API zend_class_entry *zend_ce_arithmetic_error; |
42 | | ZEND_API zend_class_entry *zend_ce_division_by_zero_error; |
43 | | |
44 | | /* Internal pseudo-exception that is not exposed to userland. */ |
45 | | static zend_class_entry zend_ce_unwind_exit; |
46 | | |
47 | | ZEND_API void (*zend_throw_exception_hook)(zval *ex); |
48 | | |
49 | | static zend_object_handlers default_exception_handlers; |
50 | | |
51 | | /* {{{ zend_implement_throwable */ |
52 | | static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type) |
53 | 100k | { |
54 | 100k | if (instanceof_function(class_type, zend_ce_exception) || instanceof_function(class_type, zend_ce_error)) { |
55 | 100k | return SUCCESS; |
56 | 100k | } |
57 | 0 | zend_error_noreturn(E_ERROR, "Class %s cannot implement interface %s, extend %s or %s instead", |
58 | 0 | ZSTR_VAL(class_type->name), |
59 | 0 | ZSTR_VAL(interface->name), |
60 | 0 | ZSTR_VAL(zend_ce_exception->name), |
61 | 0 | ZSTR_VAL(zend_ce_error->name)); |
62 | 0 | return FAILURE; |
63 | 0 | } |
64 | | /* }}} */ |
65 | | |
66 | | static inline zend_class_entry *i_get_exception_base(zval *object) /* {{{ */ |
67 | 1.55M | { |
68 | 1.55M | return instanceof_function(Z_OBJCE_P(object), zend_ce_exception) ? zend_ce_exception : zend_ce_error; |
69 | 1.55M | } |
70 | | /* }}} */ |
71 | | |
72 | | ZEND_API zend_class_entry *zend_get_exception_base(zval *object) /* {{{ */ |
73 | 0 | { |
74 | 0 | return i_get_exception_base(object); |
75 | 0 | } |
76 | | /* }}} */ |
77 | | |
78 | | void zend_exception_set_previous(zend_object *exception, zend_object *add_previous) /* {{{ */ |
79 | 1.55M | { |
80 | 1.55M | zval *previous, *ancestor, *ex; |
81 | 1.55M | zval pv, zv, rv; |
82 | 1.55M | zend_class_entry *base_ce; |
83 | | |
84 | 1.55M | if (!exception || !add_previous) { |
85 | 1.55M | return; |
86 | 1.55M | } |
87 | | |
88 | 0 | if (exception == add_previous || zend_is_unwind_exit(add_previous)) { |
89 | 0 | OBJ_RELEASE(add_previous); |
90 | 0 | return; |
91 | 0 | } |
92 | | |
93 | 0 | ZVAL_OBJ(&pv, add_previous); |
94 | 0 | if (!instanceof_function(Z_OBJCE(pv), zend_ce_throwable)) { |
95 | 0 | zend_error_noreturn(E_CORE_ERROR, "Previous exception must implement Throwable"); |
96 | 0 | return; |
97 | 0 | } |
98 | 0 | ZVAL_OBJ(&zv, exception); |
99 | 0 | ex = &zv; |
100 | 0 | do { |
101 | 0 | ancestor = zend_read_property_ex(i_get_exception_base(&pv), &pv, ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
102 | 0 | while (Z_TYPE_P(ancestor) == IS_OBJECT) { |
103 | 0 | if (Z_OBJ_P(ancestor) == Z_OBJ_P(ex)) { |
104 | 0 | OBJ_RELEASE(add_previous); |
105 | 0 | return; |
106 | 0 | } |
107 | 0 | ancestor = zend_read_property_ex(i_get_exception_base(ancestor), ancestor, ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
108 | 0 | } |
109 | 0 | base_ce = i_get_exception_base(ex); |
110 | 0 | previous = zend_read_property_ex(base_ce, ex, ZSTR_KNOWN(ZEND_STR_PREVIOUS), 1, &rv); |
111 | 0 | if (Z_TYPE_P(previous) == IS_NULL) { |
112 | 0 | zend_update_property_ex(base_ce, ex, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &pv); |
113 | 0 | GC_DELREF(add_previous); |
114 | 0 | return; |
115 | 0 | } |
116 | 0 | ex = previous; |
117 | 0 | } while (Z_OBJ_P(ex) != add_previous); |
118 | 0 | } |
119 | | /* }}} */ |
120 | | |
121 | | void zend_exception_save(void) /* {{{ */ |
122 | 1.41M | { |
123 | 1.41M | if (EG(prev_exception)) { |
124 | 0 | zend_exception_set_previous(EG(exception), EG(prev_exception)); |
125 | 0 | } |
126 | 1.41M | if (EG(exception)) { |
127 | 0 | EG(prev_exception) = EG(exception); |
128 | 0 | } |
129 | 1.41M | EG(exception) = NULL; |
130 | 1.41M | } |
131 | | /* }}} */ |
132 | | |
133 | | void zend_exception_restore(void) /* {{{ */ |
134 | 1.41M | { |
135 | 1.41M | if (EG(prev_exception)) { |
136 | 0 | if (EG(exception)) { |
137 | 0 | zend_exception_set_previous(EG(exception), EG(prev_exception)); |
138 | 0 | } else { |
139 | 0 | EG(exception) = EG(prev_exception); |
140 | 0 | } |
141 | 0 | EG(prev_exception) = NULL; |
142 | 0 | } |
143 | 1.41M | } |
144 | | /* }}} */ |
145 | | |
146 | | ZEND_API ZEND_COLD void zend_throw_exception_internal(zval *exception) /* {{{ */ |
147 | 1.55M | { |
148 | | #ifdef HAVE_DTRACE |
149 | | if (DTRACE_EXCEPTION_THROWN_ENABLED()) { |
150 | | if (exception != NULL) { |
151 | | DTRACE_EXCEPTION_THROWN(ZSTR_VAL(Z_OBJ_P(exception)->ce->name)); |
152 | | } else { |
153 | | DTRACE_EXCEPTION_THROWN(NULL); |
154 | | } |
155 | | } |
156 | | #endif /* HAVE_DTRACE */ |
157 | | |
158 | 1.55M | if (exception != NULL) { |
159 | 1.55M | zend_object *previous = EG(exception); |
160 | 1.55M | zend_exception_set_previous(Z_OBJ_P(exception), EG(exception)); |
161 | 1.55M | EG(exception) = Z_OBJ_P(exception); |
162 | 1.55M | if (previous) { |
163 | 0 | return; |
164 | 0 | } |
165 | 1.55M | } |
166 | 1.55M | if (!EG(current_execute_data)) { |
167 | 1.55M | if (exception && (Z_OBJCE_P(exception) == zend_ce_parse_error || Z_OBJCE_P(exception) == zend_ce_compile_error)) { |
168 | 1.55M | return; |
169 | 1.55M | } |
170 | 128 | if (EG(exception)) { |
171 | 128 | zend_exception_error(EG(exception), E_ERROR); |
172 | 128 | zend_bailout(); |
173 | 128 | } |
174 | 0 | zend_error_noreturn(E_CORE_ERROR, "Exception thrown without a stack frame"); |
175 | 0 | } |
176 | | |
177 | 0 | if (zend_throw_exception_hook) { |
178 | 0 | zend_throw_exception_hook(exception); |
179 | 0 | } |
180 | |
|
181 | 0 | if (!EG(current_execute_data)->func || |
182 | 0 | !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) || |
183 | 0 | EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { |
184 | | /* no need to rethrow the exception */ |
185 | 0 | return; |
186 | 0 | } |
187 | 0 | EG(opline_before_exception) = EG(current_execute_data)->opline; |
188 | 0 | EG(current_execute_data)->opline = EG(exception_op); |
189 | 0 | } |
190 | | /* }}} */ |
191 | | |
192 | | ZEND_API void zend_clear_exception(void) /* {{{ */ |
193 | 1.40M | { |
194 | 1.40M | zend_object *exception; |
195 | 1.40M | if (EG(prev_exception)) { |
196 | 0 | OBJ_RELEASE(EG(prev_exception)); |
197 | 0 | EG(prev_exception) = NULL; |
198 | 0 | } |
199 | 1.40M | if (!EG(exception)) { |
200 | 0 | return; |
201 | 0 | } |
202 | | /* exception may have destructor */ |
203 | 1.40M | exception = EG(exception); |
204 | 1.40M | EG(exception) = NULL; |
205 | 1.40M | OBJ_RELEASE(exception); |
206 | 1.40M | if (EG(current_execute_data)) { |
207 | 0 | EG(current_execute_data)->opline = EG(opline_before_exception); |
208 | 0 | } |
209 | | #if ZEND_DEBUG |
210 | | EG(opline_before_exception) = NULL; |
211 | | #endif |
212 | 1.40M | } |
213 | | /* }}} */ |
214 | | |
215 | | static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces) /* {{{ */ |
216 | 1.55M | { |
217 | 1.55M | zval obj, tmp; |
218 | 1.55M | zend_object *object; |
219 | 1.55M | zval trace; |
220 | 1.55M | zend_class_entry *base_ce; |
221 | 1.55M | zend_string *filename; |
222 | | |
223 | 1.55M | Z_OBJ(obj) = object = zend_objects_new(class_type); |
224 | 1.55M | Z_OBJ_HT(obj) = &default_exception_handlers; |
225 | | |
226 | 1.55M | object_properties_init(object, class_type); |
227 | | |
228 | 1.55M | if (EG(current_execute_data)) { |
229 | 0 | zend_fetch_debug_backtrace(&trace, |
230 | 0 | skip_top_traces, |
231 | 0 | EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0); |
232 | 1.55M | } else { |
233 | 1.55M | array_init(&trace); |
234 | 1.55M | } |
235 | 1.55M | Z_SET_REFCOUNT(trace, 0); |
236 | | |
237 | 1.55M | base_ce = i_get_exception_base(&obj); |
238 | | |
239 | 1.55M | if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error) |
240 | 128 | || !(filename = zend_get_compiled_filename()))) { |
241 | 128 | ZVAL_STRING(&tmp, zend_get_executed_filename()); |
242 | 128 | zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
243 | 128 | zval_ptr_dtor(&tmp); |
244 | 128 | ZVAL_LONG(&tmp, zend_get_executed_lineno()); |
245 | 128 | zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
246 | 1.55M | } else { |
247 | 1.55M | ZVAL_STR(&tmp, filename); |
248 | 1.55M | zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
249 | 1.55M | ZVAL_LONG(&tmp, zend_get_compiled_lineno()); |
250 | 1.55M | zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
251 | 1.55M | } |
252 | 1.55M | zend_update_property_ex(base_ce, &obj, ZSTR_KNOWN(ZEND_STR_TRACE), &trace); |
253 | | |
254 | 1.55M | return object; |
255 | 1.55M | } |
256 | | /* }}} */ |
257 | | |
258 | | static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */ |
259 | 1.55M | { |
260 | 1.55M | return zend_default_exception_new_ex(class_type, 0); |
261 | 1.55M | } |
262 | | /* }}} */ |
263 | | |
264 | | static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{{ */ |
265 | 0 | { |
266 | 0 | return zend_default_exception_new_ex(class_type, 2); |
267 | 0 | } |
268 | | /* }}} */ |
269 | | |
270 | | /* {{{ proto Exception|Error Exception|Error::__clone() |
271 | | Clone the exception object */ |
272 | | ZEND_COLD ZEND_METHOD(Exception, __clone) |
273 | 0 | { |
274 | | /* Should never be executable */ |
275 | 0 | zend_throw_exception(NULL, "Cannot clone object using __clone()", 0); |
276 | 0 | } |
277 | | /* }}} */ |
278 | | |
279 | | /* {{{ proto Exception|Error::__construct(string message, int code [, Throwable previous]) |
280 | | Exception constructor */ |
281 | | ZEND_METHOD(Exception, __construct) |
282 | 0 | { |
283 | 0 | zend_string *message = NULL; |
284 | 0 | zend_long code = 0; |
285 | 0 | zval tmp, *object, *previous = NULL; |
286 | 0 | zend_class_entry *base_ce; |
287 | |
|
288 | 0 | object = ZEND_THIS; |
289 | 0 | base_ce = i_get_exception_base(object); |
290 | |
|
291 | 0 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { |
292 | 0 | RETURN_THROWS(); |
293 | 0 | } |
294 | |
|
295 | 0 | if (message) { |
296 | 0 | ZVAL_STR(&tmp, message); |
297 | 0 | zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); |
298 | 0 | } |
299 | |
|
300 | 0 | if (code) { |
301 | 0 | ZVAL_LONG(&tmp, code); |
302 | 0 | zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); |
303 | 0 | } |
304 | |
|
305 | 0 | if (previous) { |
306 | 0 | zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); |
307 | 0 | } |
308 | 0 | } |
309 | | /* }}} */ |
310 | | |
311 | | /* {{{ proto Exception::__wakeup() |
312 | | Exception unserialize checks */ |
313 | | #define CHECK_EXC_TYPE(id, type) \ |
314 | 0 | pvalue = zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 1, &value); \ |
315 | 0 | if (Z_TYPE_P(pvalue) != IS_NULL && Z_TYPE_P(pvalue) != type) { \ |
316 | 0 | zend_unset_property(i_get_exception_base(object), object, ZSTR_VAL(ZSTR_KNOWN(id)), ZSTR_LEN(ZSTR_KNOWN(id))); \ |
317 | 0 | } |
318 | | |
319 | | ZEND_METHOD(Exception, __wakeup) |
320 | 0 | { |
321 | 0 | zval value, *pvalue; |
322 | 0 | zval *object = ZEND_THIS; |
323 | 0 | CHECK_EXC_TYPE(ZEND_STR_MESSAGE, IS_STRING); |
324 | 0 | CHECK_EXC_TYPE(ZEND_STR_STRING, IS_STRING); |
325 | 0 | CHECK_EXC_TYPE(ZEND_STR_CODE, IS_LONG); |
326 | 0 | CHECK_EXC_TYPE(ZEND_STR_FILE, IS_STRING); |
327 | 0 | CHECK_EXC_TYPE(ZEND_STR_LINE, IS_LONG); |
328 | | /* The type of $trace and $previous is enforced through typed properties. */ |
329 | 0 | } |
330 | | /* }}} */ |
331 | | |
332 | | /* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Throwable previous]]]) |
333 | | ErrorException constructor */ |
334 | | ZEND_METHOD(ErrorException, __construct) |
335 | 0 | { |
336 | 0 | zend_string *message = NULL, *filename = NULL; |
337 | 0 | zend_long code = 0, severity = E_ERROR, lineno; |
338 | 0 | zval tmp, *object, *previous = NULL; |
339 | 0 | int argc = ZEND_NUM_ARGS(); |
340 | |
|
341 | 0 | if (zend_parse_parameters(argc, "|SllSlO!", &message, &code, &severity, &filename, &lineno, &previous, zend_ce_throwable) == FAILURE) { |
342 | 0 | RETURN_THROWS(); |
343 | 0 | } |
344 | |
|
345 | 0 | object = ZEND_THIS; |
346 | |
|
347 | 0 | if (message) { |
348 | 0 | ZVAL_STR_COPY(&tmp, message); |
349 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); |
350 | 0 | zval_ptr_dtor(&tmp); |
351 | 0 | } |
352 | |
|
353 | 0 | if (code) { |
354 | 0 | ZVAL_LONG(&tmp, code); |
355 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); |
356 | 0 | } |
357 | |
|
358 | 0 | if (previous) { |
359 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); |
360 | 0 | } |
361 | |
|
362 | 0 | ZVAL_LONG(&tmp, severity); |
363 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); |
364 | |
|
365 | 0 | if (argc >= 4) { |
366 | 0 | ZVAL_STR_COPY(&tmp, filename); |
367 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp); |
368 | 0 | zval_ptr_dtor(&tmp); |
369 | 0 | if (argc < 5) { |
370 | 0 | lineno = 0; /* invalidate lineno */ |
371 | 0 | } |
372 | 0 | ZVAL_LONG(&tmp, lineno); |
373 | 0 | zend_update_property_ex(zend_ce_exception, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp); |
374 | 0 | } |
375 | 0 | } |
376 | | /* }}} */ |
377 | | |
378 | | #define GET_PROPERTY(object, id) \ |
379 | 640 | zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 0, &rv) |
380 | | #define GET_PROPERTY_SILENT(object, id) \ |
381 | 384 | zend_read_property_ex(i_get_exception_base(object), (object), ZSTR_KNOWN(id), 1, &rv) |
382 | | |
383 | | /* {{{ proto string Exception|Error::getFile() |
384 | | Get the file in which the exception occurred */ |
385 | | ZEND_METHOD(Exception, getFile) |
386 | 0 | { |
387 | 0 | zval *prop, rv; |
388 | |
|
389 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
390 | |
|
391 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_FILE); |
392 | 0 | RETURN_STR(zval_get_string(prop)); |
393 | 0 | } |
394 | | /* }}} */ |
395 | | |
396 | | /* {{{ proto int Exception|Error::getLine() |
397 | | Get the line in which the exception occurred */ |
398 | | ZEND_METHOD(Exception, getLine) |
399 | 0 | { |
400 | 0 | zval *prop, rv; |
401 | |
|
402 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
403 | |
|
404 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_LINE); |
405 | 0 | RETURN_LONG(zval_get_long(prop)); |
406 | 0 | } |
407 | | /* }}} */ |
408 | | |
409 | | /* {{{ proto string Exception|Error::getMessage() |
410 | | Get the exception message */ |
411 | | ZEND_METHOD(Exception, getMessage) |
412 | 0 | { |
413 | 0 | zval *prop, rv; |
414 | |
|
415 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
416 | |
|
417 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_MESSAGE); |
418 | 0 | RETURN_STR(zval_get_string(prop)); |
419 | 0 | } |
420 | | /* }}} */ |
421 | | |
422 | | /* {{{ proto int Exception|Error::getCode() |
423 | | Get the exception code */ |
424 | | ZEND_METHOD(Exception, getCode) |
425 | 0 | { |
426 | 0 | zval *prop, rv; |
427 | |
|
428 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
429 | |
|
430 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_CODE); |
431 | 0 | ZVAL_DEREF(prop); |
432 | 0 | ZVAL_COPY(return_value, prop); |
433 | 0 | } |
434 | | /* }}} */ |
435 | | |
436 | | /* {{{ proto array Exception|Error::getTrace() |
437 | | Get the stack trace for the location in which the exception occurred */ |
438 | | ZEND_METHOD(Exception, getTrace) |
439 | 0 | { |
440 | 0 | zval *prop, rv; |
441 | |
|
442 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
443 | |
|
444 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_TRACE); |
445 | 0 | ZVAL_DEREF(prop); |
446 | 0 | ZVAL_COPY(return_value, prop); |
447 | 0 | } |
448 | | /* }}} */ |
449 | | |
450 | | /* {{{ proto int ErrorException::getSeverity() |
451 | | Get the exception severity */ |
452 | | ZEND_METHOD(ErrorException, getSeverity) |
453 | 0 | { |
454 | 0 | zval *prop, rv; |
455 | |
|
456 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
457 | |
|
458 | 0 | prop = GET_PROPERTY(ZEND_THIS, ZEND_STR_SEVERITY); |
459 | 0 | ZVAL_DEREF(prop); |
460 | 0 | ZVAL_COPY(return_value, prop); |
461 | 0 | } |
462 | | /* }}} */ |
463 | | |
464 | 0 | #define TRACE_APPEND_KEY(key) do { \ |
465 | 0 | tmp = zend_hash_find(ht, key); \ |
466 | 0 | if (tmp) { \ |
467 | 0 | if (Z_TYPE_P(tmp) != IS_STRING) { \ |
468 | 0 | zend_error(E_WARNING, "Value for %s is not a string", \ |
469 | 0 | ZSTR_VAL(key)); \ |
470 | 0 | smart_str_appends(str, "[unknown]"); \ |
471 | 0 | } else { \ |
472 | 0 | smart_str_appends(str, Z_STRVAL_P(tmp)); \ |
473 | 0 | } \ |
474 | 0 | } \ |
475 | 0 | } while (0) |
476 | | |
477 | | static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ |
478 | 0 | { |
479 | | /* the trivial way would be to do |
480 | | * convert_to_string_ex(arg); |
481 | | * append it and kill the now tmp arg. |
482 | | * but that could cause some E_NOTICE and also damn long lines. |
483 | | */ |
484 | |
|
485 | 0 | ZVAL_DEREF(arg); |
486 | 0 | switch (Z_TYPE_P(arg)) { |
487 | 0 | case IS_NULL: |
488 | 0 | smart_str_appends(str, "NULL, "); |
489 | 0 | break; |
490 | 0 | case IS_STRING: |
491 | 0 | smart_str_appendc(str, '\''); |
492 | 0 | smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), 15)); |
493 | 0 | if (Z_STRLEN_P(arg) > 15) { |
494 | 0 | smart_str_appends(str, "...', "); |
495 | 0 | } else { |
496 | 0 | smart_str_appends(str, "', "); |
497 | 0 | } |
498 | 0 | break; |
499 | 0 | case IS_FALSE: |
500 | 0 | smart_str_appends(str, "false, "); |
501 | 0 | break; |
502 | 0 | case IS_TRUE: |
503 | 0 | smart_str_appends(str, "true, "); |
504 | 0 | break; |
505 | 0 | case IS_RESOURCE: |
506 | 0 | smart_str_appends(str, "Resource id #"); |
507 | 0 | smart_str_append_long(str, Z_RES_HANDLE_P(arg)); |
508 | 0 | smart_str_appends(str, ", "); |
509 | 0 | break; |
510 | 0 | case IS_LONG: |
511 | 0 | smart_str_append_long(str, Z_LVAL_P(arg)); |
512 | 0 | smart_str_appends(str, ", "); |
513 | 0 | break; |
514 | 0 | case IS_DOUBLE: { |
515 | 0 | smart_str_append_printf(str, "%.*G", (int) EG(precision), Z_DVAL_P(arg)); |
516 | 0 | smart_str_appends(str, ", "); |
517 | 0 | break; |
518 | 0 | } |
519 | 0 | case IS_ARRAY: |
520 | 0 | smart_str_appends(str, "Array, "); |
521 | 0 | break; |
522 | 0 | case IS_OBJECT: { |
523 | 0 | zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg)); |
524 | 0 | smart_str_appends(str, "Object("); |
525 | 0 | smart_str_appends(str, ZSTR_VAL(class_name)); |
526 | 0 | smart_str_appends(str, "), "); |
527 | 0 | zend_string_release_ex(class_name, 0); |
528 | 0 | break; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | | /* }}} */ |
533 | | |
534 | | static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /* {{{ */ |
535 | 0 | { |
536 | 0 | zval *file, *tmp; |
537 | |
|
538 | 0 | smart_str_appendc(str, '#'); |
539 | 0 | smart_str_append_long(str, num); |
540 | 0 | smart_str_appendc(str, ' '); |
541 | |
|
542 | 0 | file = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_FILE), 1); |
543 | 0 | if (file) { |
544 | 0 | if (Z_TYPE_P(file) != IS_STRING) { |
545 | 0 | zend_error(E_WARNING, "Function name is not a string"); |
546 | 0 | smart_str_appends(str, "[unknown function]"); |
547 | 0 | } else{ |
548 | 0 | zend_long line; |
549 | 0 | tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_LINE), 1); |
550 | 0 | if (tmp) { |
551 | 0 | if (Z_TYPE_P(tmp) == IS_LONG) { |
552 | 0 | line = Z_LVAL_P(tmp); |
553 | 0 | } else { |
554 | 0 | zend_error(E_WARNING, "Line is not an int"); |
555 | 0 | line = 0; |
556 | 0 | } |
557 | 0 | } else { |
558 | 0 | line = 0; |
559 | 0 | } |
560 | 0 | smart_str_append(str, Z_STR_P(file)); |
561 | 0 | smart_str_appendc(str, '('); |
562 | 0 | smart_str_append_long(str, line); |
563 | 0 | smart_str_appends(str, "): "); |
564 | 0 | } |
565 | 0 | } else { |
566 | 0 | smart_str_appends(str, "[internal function]: "); |
567 | 0 | } |
568 | 0 | TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_CLASS)); |
569 | 0 | TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_TYPE)); |
570 | 0 | TRACE_APPEND_KEY(ZSTR_KNOWN(ZEND_STR_FUNCTION)); |
571 | 0 | smart_str_appendc(str, '('); |
572 | 0 | tmp = zend_hash_find_ex(ht, ZSTR_KNOWN(ZEND_STR_ARGS), 1); |
573 | 0 | if (tmp) { |
574 | 0 | if (Z_TYPE_P(tmp) == IS_ARRAY) { |
575 | 0 | size_t last_len = ZSTR_LEN(str->s); |
576 | 0 | zval *arg; |
577 | |
|
578 | 0 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) { |
579 | 0 | _build_trace_args(arg, str); |
580 | 0 | } ZEND_HASH_FOREACH_END(); |
581 | |
|
582 | 0 | if (last_len != ZSTR_LEN(str->s)) { |
583 | 0 | ZSTR_LEN(str->s) -= 2; /* remove last ', ' */ |
584 | 0 | } |
585 | 0 | } else { |
586 | 0 | zend_error(E_WARNING, "args element is not an array"); |
587 | 0 | } |
588 | 0 | } |
589 | 0 | smart_str_appends(str, ")\n"); |
590 | 0 | } |
591 | | /* }}} */ |
592 | | |
593 | | /* {{{ proto string Exception|Error::getTraceAsString() |
594 | | Obtain the backtrace for the exception as a string (instead of an array) */ |
595 | | ZEND_METHOD(Exception, getTraceAsString) |
596 | 128 | { |
597 | 128 | zval *trace, *frame, rv; |
598 | 128 | zend_ulong index; |
599 | 128 | zval *object; |
600 | 128 | zend_class_entry *base_ce; |
601 | 128 | smart_str str = {0}; |
602 | 128 | uint32_t num = 0; |
603 | | |
604 | 128 | ZEND_PARSE_PARAMETERS_NONE(); |
605 | | |
606 | 128 | object = ZEND_THIS; |
607 | 128 | base_ce = i_get_exception_base(object); |
608 | | |
609 | 128 | trace = zend_read_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv); |
610 | 128 | if (EG(exception)) { |
611 | 0 | RETURN_THROWS(); |
612 | 0 | } |
613 | | |
614 | | /* Type should be guaranteed by property type. */ |
615 | 128 | ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY); |
616 | 128 | ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) { |
617 | 0 | if (Z_TYPE_P(frame) != IS_ARRAY) { |
618 | 0 | zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index); |
619 | 0 | continue; |
620 | 0 | } |
621 | | |
622 | 0 | _build_trace_string(&str, Z_ARRVAL_P(frame), num++); |
623 | 0 | } ZEND_HASH_FOREACH_END(); |
624 | | |
625 | 128 | smart_str_appendc(&str, '#'); |
626 | 128 | smart_str_append_long(&str, num); |
627 | 128 | smart_str_appends(&str, " {main}"); |
628 | 128 | smart_str_0(&str); |
629 | | |
630 | 128 | RETURN_NEW_STR(str.s); |
631 | 128 | } |
632 | | /* }}} */ |
633 | | |
634 | | /* {{{ proto Throwable Exception|Error::getPrevious() |
635 | | Return previous Throwable or NULL. */ |
636 | | ZEND_METHOD(Exception, getPrevious) |
637 | 0 | { |
638 | 0 | zval rv; |
639 | |
|
640 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
641 | |
|
642 | 0 | ZVAL_COPY(return_value, GET_PROPERTY_SILENT(ZEND_THIS, ZEND_STR_PREVIOUS)); |
643 | 0 | } /* }}} */ |
644 | | |
645 | | /* {{{ proto string Exception|Error::__toString() |
646 | | Obtain the string representation of the Exception object */ |
647 | | ZEND_METHOD(Exception, __toString) |
648 | 128 | { |
649 | 128 | zval trace, *exception; |
650 | 128 | zend_class_entry *base_ce; |
651 | 128 | zend_string *str; |
652 | 128 | zend_fcall_info fci; |
653 | 128 | zval rv, tmp; |
654 | 128 | zend_string *fname; |
655 | | |
656 | 128 | ZEND_PARSE_PARAMETERS_NONE(); |
657 | | |
658 | 128 | str = ZSTR_EMPTY_ALLOC(); |
659 | | |
660 | 128 | exception = ZEND_THIS; |
661 | 128 | fname = zend_string_init("gettraceasstring", sizeof("gettraceasstring")-1, 0); |
662 | | |
663 | 256 | while (exception && Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) { |
664 | 128 | zend_string *prev_str = str; |
665 | 128 | zend_string *message = zval_get_string(GET_PROPERTY(exception, ZEND_STR_MESSAGE)); |
666 | 128 | zend_string *file = zval_get_string(GET_PROPERTY(exception, ZEND_STR_FILE)); |
667 | 128 | zend_long line = zval_get_long(GET_PROPERTY(exception, ZEND_STR_LINE)); |
668 | | |
669 | 128 | fci.size = sizeof(fci); |
670 | 128 | ZVAL_STR(&fci.function_name, fname); |
671 | 128 | fci.object = Z_OBJ_P(exception); |
672 | 128 | fci.retval = &trace; |
673 | 128 | fci.param_count = 0; |
674 | 128 | fci.params = NULL; |
675 | 128 | fci.no_separation = 1; |
676 | | |
677 | 128 | zend_call_function(&fci, NULL); |
678 | | |
679 | 128 | if (Z_TYPE(trace) != IS_STRING) { |
680 | 0 | zval_ptr_dtor(&trace); |
681 | 0 | ZVAL_UNDEF(&trace); |
682 | 0 | } |
683 | | |
684 | 128 | if ((Z_OBJCE_P(exception) == zend_ce_type_error || Z_OBJCE_P(exception) == zend_ce_argument_count_error) && strstr(ZSTR_VAL(message), ", called in ")) { |
685 | 0 | zend_string *real_message = zend_strpprintf(0, "%s and defined", ZSTR_VAL(message)); |
686 | 0 | zend_string_release_ex(message, 0); |
687 | 0 | message = real_message; |
688 | 0 | } |
689 | | |
690 | 128 | if (ZSTR_LEN(message) > 0) { |
691 | 128 | str = zend_strpprintf(0, "%s: %s in %s:" ZEND_LONG_FMT |
692 | 128 | "\nStack trace:\n%s%s%s", |
693 | 128 | ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(message), ZSTR_VAL(file), line, |
694 | 128 | (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n", |
695 | 128 | ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str)); |
696 | 0 | } else { |
697 | 0 | str = zend_strpprintf(0, "%s in %s:" ZEND_LONG_FMT |
698 | 0 | "\nStack trace:\n%s%s%s", |
699 | 0 | ZSTR_VAL(Z_OBJCE_P(exception)->name), ZSTR_VAL(file), line, |
700 | 0 | (Z_TYPE(trace) == IS_STRING && Z_STRLEN(trace)) ? Z_STRVAL(trace) : "#0 {main}\n", |
701 | 0 | ZSTR_LEN(prev_str) ? "\n\nNext " : "", ZSTR_VAL(prev_str)); |
702 | 0 | } |
703 | | |
704 | 128 | zend_string_release_ex(prev_str, 0); |
705 | 128 | zend_string_release_ex(message, 0); |
706 | 128 | zend_string_release_ex(file, 0); |
707 | 128 | zval_ptr_dtor(&trace); |
708 | | |
709 | 128 | Z_PROTECT_RECURSION_P(exception); |
710 | 128 | exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS); |
711 | 128 | if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) { |
712 | 0 | break; |
713 | 0 | } |
714 | 128 | } |
715 | 128 | zend_string_release_ex(fname, 0); |
716 | | |
717 | 128 | exception = ZEND_THIS; |
718 | | /* Reset apply counts */ |
719 | 256 | while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(exception)) && instanceof_function(Z_OBJCE_P(exception), base_ce)) { |
720 | 128 | if (Z_IS_RECURSIVE_P(exception)) { |
721 | 128 | Z_UNPROTECT_RECURSION_P(exception); |
722 | 0 | } else { |
723 | 0 | break; |
724 | 0 | } |
725 | 128 | exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS); |
726 | 128 | } |
727 | | |
728 | 128 | exception = ZEND_THIS; |
729 | 128 | base_ce = i_get_exception_base(exception); |
730 | | |
731 | | /* We store the result in the private property string so we can access |
732 | | * the result in uncaught exception handlers without memleaks. */ |
733 | 128 | ZVAL_STR(&tmp, str); |
734 | 128 | zend_update_property_ex(base_ce, exception, ZSTR_KNOWN(ZEND_STR_STRING), &tmp); |
735 | | |
736 | 128 | RETURN_STR(str); |
737 | 128 | } |
738 | | /* }}} */ |
739 | | |
740 | | static void declare_exception_properties(zend_class_entry *ce) |
741 | 7.36k | { |
742 | 7.36k | zval val; |
743 | | |
744 | 7.36k | zend_declare_property_string(ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); |
745 | 7.36k | zend_declare_property_string(ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); |
746 | 7.36k | zend_declare_property_long(ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); |
747 | 7.36k | zend_declare_property_null(ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); |
748 | 7.36k | zend_declare_property_null(ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); |
749 | | |
750 | 7.36k | ZVAL_EMPTY_ARRAY(&val); |
751 | 7.36k | zend_declare_typed_property( |
752 | 7.36k | ce, ZSTR_KNOWN(ZEND_STR_TRACE), &val, ZEND_ACC_PRIVATE, NULL, |
753 | 7.36k | (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); |
754 | | |
755 | 7.36k | ZVAL_NULL(&val); |
756 | 7.36k | zend_declare_typed_property( |
757 | 7.36k | ce, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &val, ZEND_ACC_PRIVATE, NULL, |
758 | 7.36k | (zend_type) ZEND_TYPE_INIT_CE(zend_ce_throwable, /* allow_null */ 1, 0)); |
759 | 7.36k | } |
760 | | |
761 | | void zend_register_default_exception(void) /* {{{ */ |
762 | 3.68k | { |
763 | 3.68k | zend_class_entry ce; |
764 | | |
765 | 3.68k | REGISTER_MAGIC_INTERFACE(throwable, Throwable); |
766 | 3.68k | zend_class_implements(zend_ce_throwable, 1, zend_ce_stringable); |
767 | | |
768 | 3.68k | memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers)); |
769 | 3.68k | default_exception_handlers.clone_obj = NULL; |
770 | | |
771 | 3.68k | INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods); |
772 | 3.68k | zend_ce_exception = zend_register_internal_class_ex(&ce, NULL); |
773 | 3.68k | zend_ce_exception->create_object = zend_default_exception_new; |
774 | 3.68k | zend_class_implements(zend_ce_exception, 1, zend_ce_throwable); |
775 | 3.68k | declare_exception_properties(zend_ce_exception); |
776 | | |
777 | 3.68k | INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods); |
778 | 3.68k | zend_ce_error_exception = zend_register_internal_class_ex(&ce, zend_ce_exception); |
779 | 3.68k | zend_ce_error_exception->create_object = zend_error_exception_new; |
780 | 3.68k | zend_declare_property_long(zend_ce_error_exception, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED); |
781 | | |
782 | 3.68k | INIT_CLASS_ENTRY(ce, "Error", class_Error_methods); |
783 | 3.68k | zend_ce_error = zend_register_internal_class_ex(&ce, NULL); |
784 | 3.68k | zend_ce_error->create_object = zend_default_exception_new; |
785 | 3.68k | zend_class_implements(zend_ce_error, 1, zend_ce_throwable); |
786 | 3.68k | declare_exception_properties(zend_ce_error); |
787 | | |
788 | 3.68k | INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods); |
789 | 3.68k | zend_ce_compile_error = zend_register_internal_class_ex(&ce, zend_ce_error); |
790 | 3.68k | zend_ce_compile_error->create_object = zend_default_exception_new; |
791 | | |
792 | 3.68k | INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods); |
793 | 3.68k | zend_ce_parse_error = zend_register_internal_class_ex(&ce, zend_ce_compile_error); |
794 | 3.68k | zend_ce_parse_error->create_object = zend_default_exception_new; |
795 | | |
796 | 3.68k | INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods); |
797 | 3.68k | zend_ce_type_error = zend_register_internal_class_ex(&ce, zend_ce_error); |
798 | 3.68k | zend_ce_type_error->create_object = zend_default_exception_new; |
799 | | |
800 | 3.68k | INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods); |
801 | 3.68k | zend_ce_argument_count_error = zend_register_internal_class_ex(&ce, zend_ce_type_error); |
802 | 3.68k | zend_ce_argument_count_error->create_object = zend_default_exception_new; |
803 | | |
804 | 3.68k | INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods); |
805 | 3.68k | zend_ce_value_error = zend_register_internal_class_ex(&ce, zend_ce_error); |
806 | 3.68k | zend_ce_value_error->create_object = zend_default_exception_new; |
807 | | |
808 | 3.68k | INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods); |
809 | 3.68k | zend_ce_arithmetic_error = zend_register_internal_class_ex(&ce, zend_ce_error); |
810 | 3.68k | zend_ce_arithmetic_error->create_object = zend_default_exception_new; |
811 | | |
812 | 3.68k | INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods); |
813 | 3.68k | zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error); |
814 | 3.68k | zend_ce_division_by_zero_error->create_object = zend_default_exception_new; |
815 | | |
816 | 3.68k | INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); |
817 | 3.68k | } |
818 | | /* }}} */ |
819 | | |
820 | | /* {{{ Deprecated - Use zend_ce_exception directly instead */ |
821 | | ZEND_API zend_class_entry *zend_exception_get_default(void) |
822 | 0 | { |
823 | 0 | return zend_ce_exception; |
824 | 0 | } |
825 | | /* }}} */ |
826 | | |
827 | | /* {{{ Deprecated - Use zend_ce_error_exception directly instead */ |
828 | | ZEND_API zend_class_entry *zend_get_error_exception(void) |
829 | 0 | { |
830 | 0 | return zend_ce_error_exception; |
831 | 0 | } |
832 | | /* }}} */ |
833 | | |
834 | | static zend_object *zend_throw_exception_zstr(zend_class_entry *exception_ce, zend_string *message, zend_long code) /* {{{ */ |
835 | 1.55M | { |
836 | 1.55M | zval ex, tmp; |
837 | | |
838 | 1.55M | if (exception_ce) { |
839 | 1.55M | if (!instanceof_function(exception_ce, zend_ce_throwable)) { |
840 | 0 | zend_error(E_NOTICE, "Exceptions must implement Throwable"); |
841 | 0 | exception_ce = zend_ce_exception; |
842 | 0 | } |
843 | 0 | } else { |
844 | 0 | exception_ce = zend_ce_exception; |
845 | 0 | } |
846 | 1.55M | object_init_ex(&ex, exception_ce); |
847 | | |
848 | | |
849 | 1.55M | if (message) { |
850 | 1.55M | ZVAL_STR(&tmp, message); |
851 | 1.55M | zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); |
852 | 1.55M | } |
853 | 1.55M | if (code) { |
854 | 0 | ZVAL_LONG(&tmp, code); |
855 | 0 | zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); |
856 | 0 | } |
857 | | |
858 | 1.55M | zend_throw_exception_internal(&ex); |
859 | 1.55M | return Z_OBJ(ex); |
860 | 1.55M | } |
861 | | /* }}} */ |
862 | | |
863 | | ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */ |
864 | 1.55M | { |
865 | 1.55M | zend_string *msg_str = message ? zend_string_init(message, strlen(message), 0) : NULL; |
866 | 1.55M | zend_object *ex = zend_throw_exception_zstr(exception_ce, msg_str, code); |
867 | 1.55M | if (msg_str) { |
868 | 1.55M | zend_string_release(msg_str); |
869 | 1.55M | } |
870 | 1.55M | return ex; |
871 | 1.55M | } |
872 | | /* }}} */ |
873 | | |
874 | | ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format, ...) /* {{{ */ |
875 | 23.9k | { |
876 | 23.9k | va_list arg; |
877 | 23.9k | char *message; |
878 | 23.9k | zend_object *obj; |
879 | | |
880 | 23.9k | va_start(arg, format); |
881 | 23.9k | zend_vspprintf(&message, 0, format, arg); |
882 | 23.9k | va_end(arg); |
883 | 23.9k | obj = zend_throw_exception(exception_ce, message, code); |
884 | 23.9k | efree(message); |
885 | 23.9k | return obj; |
886 | 23.9k | } |
887 | | /* }}} */ |
888 | | |
889 | | ZEND_API ZEND_COLD zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, zend_string *message, zend_long code, int severity) /* {{{ */ |
890 | 0 | { |
891 | 0 | zval ex, tmp; |
892 | 0 | zend_object *obj = zend_throw_exception_zstr(exception_ce, message, code); |
893 | 0 | ZVAL_OBJ(&ex, obj); |
894 | 0 | ZVAL_LONG(&tmp, severity); |
895 | 0 | zend_update_property_ex(zend_ce_error_exception, &ex, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); |
896 | 0 | return obj; |
897 | 0 | } |
898 | | /* }}} */ |
899 | | |
900 | | static void zend_error_va(int type, const char *file, uint32_t lineno, const char *format, ...) /* {{{ */ |
901 | 128 | { |
902 | 128 | va_list args; |
903 | 128 | va_start(args, format); |
904 | 128 | zend_string *message = zend_vstrpprintf(0, format, args); |
905 | 128 | zend_error_cb(type, file, lineno, message); |
906 | 128 | zend_string_release(message); |
907 | 128 | va_end(args); |
908 | 128 | } |
909 | | /* }}} */ |
910 | | |
911 | | /* This function doesn't return if it uses E_ERROR */ |
912 | | ZEND_API ZEND_COLD int zend_exception_error(zend_object *ex, int severity) /* {{{ */ |
913 | 128 | { |
914 | 128 | zval exception, rv; |
915 | 128 | zend_class_entry *ce_exception; |
916 | 128 | int result = FAILURE; |
917 | | |
918 | 128 | ZVAL_OBJ(&exception, ex); |
919 | 128 | ce_exception = ex->ce; |
920 | 128 | EG(exception) = NULL; |
921 | 128 | if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) { |
922 | 0 | zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE)); |
923 | 0 | zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE)); |
924 | 0 | zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE)); |
925 | |
|
926 | 0 | zend_error_cb( |
927 | 0 | (ce_exception == zend_ce_parse_error ? E_PARSE : E_COMPILE_ERROR) | E_DONT_BAIL, |
928 | 0 | ZSTR_VAL(file), line, message); |
929 | |
|
930 | 0 | zend_string_release_ex(file, 0); |
931 | 0 | zend_string_release_ex(message, 0); |
932 | 128 | } else if (instanceof_function(ce_exception, zend_ce_throwable)) { |
933 | 128 | zval tmp; |
934 | 128 | zend_string *str, *file = NULL; |
935 | 128 | zend_long line = 0; |
936 | | |
937 | 128 | zend_call_known_instance_method_with_0_params(ex->ce->__tostring, Z_OBJ(exception), &tmp); |
938 | 128 | if (!EG(exception)) { |
939 | 128 | if (Z_TYPE(tmp) != IS_STRING) { |
940 | 0 | zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name)); |
941 | 128 | } else { |
942 | 128 | zend_update_property_ex(i_get_exception_base(&exception), &exception, ZSTR_KNOWN(ZEND_STR_STRING), &tmp); |
943 | 128 | } |
944 | 128 | } |
945 | 128 | zval_ptr_dtor(&tmp); |
946 | | |
947 | 128 | if (EG(exception)) { |
948 | 0 | zval zv; |
949 | |
|
950 | 0 | ZVAL_OBJ(&zv, EG(exception)); |
951 | | /* do the best we can to inform about the inner exception */ |
952 | 0 | if (instanceof_function(ce_exception, zend_ce_exception) || instanceof_function(ce_exception, zend_ce_error)) { |
953 | 0 | file = zval_get_string(GET_PROPERTY_SILENT(&zv, ZEND_STR_FILE)); |
954 | 0 | line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE)); |
955 | 0 | } |
956 | |
|
957 | 0 | zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line, |
958 | 0 | "Uncaught %s in exception handling during call to %s::__tostring()", |
959 | 0 | ZSTR_VAL(Z_OBJCE(zv)->name), ZSTR_VAL(ce_exception->name)); |
960 | |
|
961 | 0 | if (file) { |
962 | 0 | zend_string_release_ex(file, 0); |
963 | 0 | } |
964 | 0 | } |
965 | | |
966 | 128 | str = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_STRING)); |
967 | 128 | file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE)); |
968 | 128 | line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE)); |
969 | | |
970 | 128 | zend_error_va(severity | E_DONT_BAIL, |
971 | 128 | (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line, |
972 | 128 | "Uncaught %s\n thrown", ZSTR_VAL(str)); |
973 | | |
974 | 128 | zend_string_release_ex(str, 0); |
975 | 128 | zend_string_release_ex(file, 0); |
976 | 0 | } else if (ce_exception == &zend_ce_unwind_exit) { |
977 | | /* We successfully unwound, nothing more to do */ |
978 | 0 | result = SUCCESS; |
979 | 0 | } else { |
980 | 0 | zend_error(severity, "Uncaught exception '%s'", ZSTR_VAL(ce_exception->name)); |
981 | 0 | } |
982 | | |
983 | 128 | OBJ_RELEASE(ex); |
984 | 128 | return result; |
985 | 128 | } |
986 | | /* }}} */ |
987 | | |
988 | | ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */ |
989 | 0 | { |
990 | 0 | zend_class_entry *exception_ce; |
991 | |
|
992 | 0 | if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) { |
993 | 0 | zend_error_noreturn(E_CORE_ERROR, "Need to supply an object when throwing an exception"); |
994 | 0 | } |
995 | | |
996 | 0 | exception_ce = Z_OBJCE_P(exception); |
997 | |
|
998 | 0 | if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) { |
999 | 0 | zend_throw_error(NULL, "Cannot throw objects that do not implement Throwable"); |
1000 | 0 | zval_ptr_dtor(exception); |
1001 | 0 | return; |
1002 | 0 | } |
1003 | 0 | zend_throw_exception_internal(exception); |
1004 | 0 | } |
1005 | | /* }}} */ |
1006 | | |
1007 | | ZEND_API ZEND_COLD void zend_throw_unwind_exit() |
1008 | 0 | { |
1009 | 0 | ZEND_ASSERT(!EG(exception)); |
1010 | 0 | EG(exception) = zend_objects_new(&zend_ce_unwind_exit); |
1011 | 0 | EG(opline_before_exception) = EG(current_execute_data)->opline; |
1012 | 0 | EG(current_execute_data)->opline = EG(exception_op); |
1013 | 0 | } |
1014 | | |
1015 | | ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex) |
1016 | 0 | { |
1017 | 0 | return ex->ce == &zend_ce_unwind_exit; |
1018 | 0 | } |