1 : /*
2 : +----------------------------------------------------------------------+
3 : | Zend Engine |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1998-2009 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@zend.com> |
16 : | Marcus Boerger <helly@php.net> |
17 : | Sterling Hughes <sterling@php.net> |
18 : | Zeev Suraski <zeev@zend.com> |
19 : +----------------------------------------------------------------------+
20 : */
21 :
22 : /* $Id: zend_exceptions.c 284424 2009-07-20 10:12:34Z dsp $ */
23 :
24 : #include "zend.h"
25 : #include "zend_API.h"
26 : #include "zend_builtin_functions.h"
27 : #include "zend_interfaces.h"
28 : #include "zend_exceptions.h"
29 : #include "zend_vm.h"
30 : #include "zend_dtrace.h"
31 :
32 : zend_class_entry *default_exception_ce;
33 : zend_class_entry *error_exception_ce;
34 : static zend_object_handlers default_exception_handlers;
35 : ZEND_API void (*zend_throw_exception_hook)(zval *ex TSRMLS_DC);
36 :
37 : void zend_exception_set_previous(zval *exception, zval *add_previous TSRMLS_DC)
38 1095 : {
39 : zval *previous;
40 :
41 1095 : if (exception == add_previous || !add_previous || !exception) {
42 1088 : return;
43 : }
44 7 : if (Z_TYPE_P(add_previous) != IS_OBJECT && !instanceof_function(Z_OBJCE_P(add_previous), default_exception_ce TSRMLS_CC)) {
45 0 : zend_error(E_ERROR, "Cannot set non exception as previous exception");
46 0 : return;
47 : }
48 14 : while (exception && exception != add_previous && Z_OBJ_HANDLE_P(exception) != Z_OBJ_HANDLE_P(add_previous)) {
49 7 : previous = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 1 TSRMLS_CC);
50 7 : if (Z_TYPE_P(previous) == IS_NULL) {
51 7 : zend_update_property(default_exception_ce, exception, "previous", sizeof("previous")-1, add_previous TSRMLS_CC);
52 7 : Z_DELREF_P(add_previous);
53 7 : return;
54 : }
55 0 : exception = previous;
56 : }
57 : }
58 :
59 : void zend_exception_save(TSRMLS_D) /* {{{ */
60 1442 : {
61 1442 : if (EG(prev_exception)) {
62 8 : zend_exception_set_previous(EG(exception), EG(prev_exception) TSRMLS_CC);
63 : }
64 1442 : if (EG(exception)) {
65 41 : EG(prev_exception) = EG(exception);
66 : }
67 1442 : EG(exception) = NULL;
68 1442 : }
69 : /* }}} */
70 :
71 : void zend_exception_restore(TSRMLS_D) /* {{{ */
72 17951 : {
73 17951 : if (EG(prev_exception)) {
74 41 : if (EG(exception)) {
75 7 : zend_exception_set_previous(EG(exception), EG(prev_exception) TSRMLS_CC);
76 : } else {
77 34 : EG(exception) = EG(prev_exception);
78 : }
79 41 : EG(prev_exception) = NULL;
80 : }
81 17951 : }
82 : /* }}} */
83 :
84 : void zend_throw_exception_internal(zval *exception TSRMLS_DC) /* {{{ */
85 2355 : {
86 : #ifdef HAVE_DTRACE
87 : if (DTRACE_EXCEPTION_THROWN_ENABLED()) {
88 : zstr classname;
89 : char *s_classname;
90 : int name_len, s_classname_len;
91 :
92 : classname = NULL_ZSTR;
93 : zend_get_object_classname(exception, &classname, &name_len TSRMLS_CC);
94 : zend_unicode_to_string(ZEND_U_CONVERTER(UG(utf8_conv)), &s_classname, &s_classname_len, classname.u, u_strlen(classname.u) TSRMLS_CC);
95 :
96 : DTRACE_EXCEPTION_THROWN(s_classname);
97 :
98 : efree(classname.v);
99 : efree(s_classname);
100 : }
101 : #endif /* HAVE_DTRACE */
102 :
103 2355 : if (exception != NULL) {
104 1080 : zval *previous = EG(exception);
105 1080 : zend_exception_set_previous(exception, EG(exception) TSRMLS_CC);
106 1080 : EG(exception) = exception;
107 1080 : if (previous) {
108 0 : return;
109 : }
110 : }
111 2355 : if (!EG(current_execute_data)) {
112 1 : zend_error(E_ERROR, "Exception thrown without a stack frame");
113 : }
114 :
115 2354 : if (zend_throw_exception_hook) {
116 0 : zend_throw_exception_hook(exception TSRMLS_CC);
117 : }
118 :
119 2354 : if (EG(current_execute_data)->opline == NULL ||
120 : (EG(current_execute_data)->opline+1)->opcode == ZEND_HANDLE_EXCEPTION) {
121 : /* no need to rethrow the exception */
122 986 : return;
123 : }
124 1368 : EG(opline_before_exception) = EG(current_execute_data)->opline;
125 1368 : EG(current_execute_data)->opline = EG(exception_op);
126 : }
127 : /* }}} */
128 :
129 : ZEND_API void zend_clear_exception(TSRMLS_D) /* {{{ */
130 95 : {
131 95 : if (EG(prev_exception)) {
132 0 : zval_ptr_dtor(&EG(prev_exception));
133 0 : EG(prev_exception) = NULL;
134 : }
135 95 : if (!EG(exception)) {
136 89 : return;
137 : }
138 6 : zval_ptr_dtor(&EG(exception));
139 6 : EG(exception) = NULL;
140 6 : EG(current_execute_data)->opline = EG(opline_before_exception);
141 : #if ZEND_DEBUG
142 : EG(opline_before_exception) = NULL;
143 : #endif
144 : }
145 : /* }}} */
146 :
147 : static zend_object_value zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces TSRMLS_DC) /* {{{ */
148 1496 : {
149 : zval tmp, obj;
150 : zend_object *object;
151 : zval *trace;
152 :
153 1496 : Z_OBJVAL(obj) = zend_objects_new(&object, class_type TSRMLS_CC);
154 1496 : Z_OBJ_HT(obj) = &default_exception_handlers;
155 :
156 1496 : ALLOC_HASHTABLE(object->properties);
157 1496 : zend_u_hash_init(object->properties, 0, NULL, ZVAL_PTR_DTOR, 0, UG(unicode));
158 1496 : zend_hash_copy(object->properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
159 :
160 1496 : ALLOC_ZVAL(trace);
161 1496 : Z_UNSET_ISREF_P(trace);
162 1496 : Z_SET_REFCOUNT_P(trace, 0);
163 1496 : zend_fetch_debug_backtrace(trace, skip_top_traces, 0 TSRMLS_CC);
164 :
165 1496 : zend_update_property_rt_string(default_exception_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename(TSRMLS_C) TSRMLS_CC);
166 1496 : zend_update_property_long(default_exception_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
167 1496 : zend_update_property(default_exception_ce, &obj, "trace", sizeof("trace")-1, trace TSRMLS_CC);
168 :
169 1496 : return Z_OBJVAL(obj);
170 : }
171 : /* }}} */
172 :
173 : static zend_object_value zend_default_exception_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
174 1495 : {
175 1495 : return zend_default_exception_new_ex(class_type, 0 TSRMLS_CC);
176 : }
177 : /* }}} */
178 :
179 : static zend_object_value zend_error_exception_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
180 1 : {
181 1 : return zend_default_exception_new_ex(class_type, 2 TSRMLS_CC);
182 : }
183 : /* }}} */
184 :
185 : /* {{{ proto Exception Exception::__clone() U
186 : Clone the exception object */
187 : ZEND_METHOD(exception, __clone)
188 0 : {
189 : /* Should never be executable */
190 0 : zend_throw_exception(NULL, "Cannot clone object using __clone()", 0 TSRMLS_CC);
191 0 : }
192 : /* }}} */
193 :
194 : /* {{{ proto Exception::__construct(string message, int code [, Exception previous]) U
195 : Exception constructor */
196 : ZEND_METHOD(exception, __construct)
197 201 : {
198 201 : void *message = NULL;
199 201 : long code = 0;
200 201 : zval *object, *previous = NULL;
201 201 : int argc = ZEND_NUM_ARGS(), message_len;
202 : zend_uchar message_type;
203 :
204 201 : if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tlO!", &message, &message_len, &message_type, &code, &previous, default_exception_ce) == FAILURE) {
205 0 : zend_error(E_ERROR, "Wrong parameters for Exception([string $exception [, long $code [, Exception $previous = NULL]]])");
206 : }
207 :
208 201 : object = getThis();
209 :
210 201 : if (message) {
211 139 : if (message_type == IS_UNICODE) {
212 139 : zend_update_property_unicodel(default_exception_ce, object, "message", sizeof("message")-1, message, message_len TSRMLS_CC);
213 : } else {
214 0 : zend_update_property_rt_stringl(default_exception_ce, object, "message", sizeof("message")-1, message, message_len TSRMLS_CC);
215 : }
216 : }
217 :
218 201 : if (code) {
219 12 : zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
220 : }
221 :
222 201 : if (previous) {
223 3 : zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
224 : }
225 201 : }
226 : /* }}} */
227 :
228 : /* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Exception previous]]]) U
229 : ErrorException constructor */
230 : ZEND_METHOD(error_exception, __construct)
231 1 : {
232 1 : void *message = NULL, *filename = NULL;
233 1 : long code = 0, severity = E_ERROR, lineno;
234 1 : zval *object, *previous = NULL;
235 1 : int argc = ZEND_NUM_ARGS(), message_len, filename_len;
236 : zend_uchar message_type, file_type;
237 :
238 1 : if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|tlltlO!", &message, &message_len, &message_type, &code, &severity, &filename, &filename_len, &file_type, &lineno, &previous, default_exception_ce) == FAILURE) {
239 0 : zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno [, Exception $previous = NULL]]]]]])");
240 : }
241 :
242 1 : object = getThis();
243 :
244 1 : if (message) {
245 1 : if (message_type == IS_UNICODE) {
246 1 : zend_update_property_unicodel(default_exception_ce, object, "message", sizeof("message")-1, message, message_len TSRMLS_CC);
247 : } else {
248 0 : zend_update_property_rt_stringl(default_exception_ce, object, "message", sizeof("message")-1, message, message_len TSRMLS_CC);
249 : }
250 : }
251 :
252 1 : if (code) {
253 0 : zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
254 : }
255 :
256 1 : if (previous) {
257 0 : zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
258 : }
259 :
260 1 : zend_update_property_long(default_exception_ce, object, "severity", sizeof("severity")-1, severity TSRMLS_CC);
261 :
262 1 : if (argc >= 4) {
263 1 : if (file_type == IS_UNICODE) {
264 1 : zend_update_property_unicodel(default_exception_ce, object, "file", sizeof("file")-1, filename, filename_len TSRMLS_CC);
265 : } else {
266 0 : zend_update_property_stringl(default_exception_ce, object, "file", sizeof("file")-1, filename, filename_len TSRMLS_CC);
267 : }
268 1 : if (argc < 5) {
269 0 : lineno = 0; /* invalidate lineno */
270 : }
271 1 : zend_update_property_long(default_exception_ce, object, "line", sizeof("line")-1, lineno TSRMLS_CC);
272 : }
273 1 : }
274 : /* }}} */
275 :
276 : #define DEFAULT_0_PARAMS \
277 : if (zend_parse_parameters_none() == FAILURE) { \
278 : return; \
279 : }
280 :
281 : static void _default_exception_get_entry(zval *object, char *name, int name_len, zval *return_value TSRMLS_DC) /* {{{ */
282 1164 : {
283 : zval *value;
284 :
285 1164 : value = zend_read_property(default_exception_ce, object, name, name_len, 0 TSRMLS_CC);
286 :
287 1164 : *return_value = *value;
288 1164 : zval_copy_ctor(return_value);
289 1164 : INIT_PZVAL(return_value);
290 1164 : }
291 : /* }}} */
292 :
293 : /* {{{ proto string Exception::getFile() U
294 : Get the file in which the exception occurred */
295 : ZEND_METHOD(exception, getFile)
296 7 : {
297 7 : DEFAULT_0_PARAMS;
298 :
299 6 : _default_exception_get_entry(getThis(), "file", sizeof("file")-1, return_value TSRMLS_CC);
300 : }
301 : /* }}} */
302 :
303 : /* {{{ proto int Exception::getLine() U
304 : Get the line in which the exception occurred */
305 : ZEND_METHOD(exception, getLine)
306 13 : {
307 13 : DEFAULT_0_PARAMS;
308 :
309 12 : _default_exception_get_entry(getThis(), "line", sizeof("line")-1, return_value TSRMLS_CC);
310 : }
311 : /* }}} */
312 :
313 : /* {{{ proto string Exception::getMessage() U
314 : Get the exception message */
315 : ZEND_METHOD(exception, getMessage)
316 840 : {
317 840 : DEFAULT_0_PARAMS;
318 :
319 839 : _default_exception_get_entry(getThis(), "message", sizeof("message")-1, return_value TSRMLS_CC);
320 : }
321 : /* }}} */
322 :
323 : /* {{{ proto int Exception::getCode() U
324 : Get the exception code */
325 : ZEND_METHOD(exception, getCode)
326 15 : {
327 15 : DEFAULT_0_PARAMS;
328 :
329 14 : _default_exception_get_entry(getThis(), "code", sizeof("code")-1, return_value TSRMLS_CC);
330 : }
331 : /* }}} */
332 :
333 : /* {{{ proto array Exception::getTrace() U
334 : Get the stack trace for the location in which the exception occurred */
335 : ZEND_METHOD(exception, getTrace)
336 3 : {
337 3 : DEFAULT_0_PARAMS;
338 :
339 2 : _default_exception_get_entry(getThis(), "trace", sizeof("trace")-1, return_value TSRMLS_CC);
340 : }
341 : /* }}} */
342 :
343 : /* {{{ proto int ErrorException::getSeverity() U
344 : Get the exception severity */
345 : ZEND_METHOD(error_exception, getSeverity)
346 0 : {
347 0 : DEFAULT_0_PARAMS;
348 :
349 0 : _default_exception_get_entry(getThis(), "severity", sizeof("severity")-1, return_value TSRMLS_CC);
350 : }
351 : /* }}} */
352 :
353 : /* {{{ gettraceasstring() macros */
354 : #define TRACE_APPEND_CHR(chr) \
355 : *str = (char*)erealloc(*str, *len + 1 + 1); \
356 : (*str)[(*len)++] = chr
357 :
358 : #define TRACE_APPEND_STRL(val, vallen) \
359 : { \
360 : int l = vallen; \
361 : *str = (char*)erealloc(*str, *len + l + 1); \
362 : memcpy((*str) + *len, val, l); \
363 : *len += l; \
364 : }
365 :
366 : #define TRACE_APPEND_USTRL(val, vallen) \
367 : { \
368 : zval tmp, copy; \
369 : int use_copy; \
370 : ZVAL_UNICODEL(&tmp, val, vallen, 1); \
371 : zend_make_printable_zval(&tmp, ©, &use_copy); \
372 : TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
373 : zval_dtor(©); \
374 : zval_dtor(&tmp); \
375 : }
376 :
377 : #define TRACE_APPEND_ZVAL(zv) \
378 : if (Z_TYPE_P((zv)) == IS_UNICODE) { \
379 : zval copy; \
380 : int use_copy; \
381 : zend_make_printable_zval((zv), ©, &use_copy); \
382 : TRACE_APPEND_STRL(Z_STRVAL_P((use_copy ? © : (zv))), Z_STRLEN_P((use_copy ? © : (zv)))); \
383 : if (use_copy) zval_dtor(©); \
384 : } else { \
385 : TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
386 : }
387 :
388 : #define TRACE_APPEND_STR(val) \
389 : TRACE_APPEND_STRL(val, sizeof(val)-1)
390 :
391 : #define TRACE_APPEND_KEY(key) \
392 : if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
393 : TRACE_APPEND_ZVAL(*tmp); \
394 : }
395 : /* }}} */
396 :
397 : static int _build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
398 191 : {
399 : char **str;
400 : int *len;
401 :
402 191 : str = va_arg(args, char**);
403 191 : len = va_arg(args, int*);
404 :
405 : /* the trivial way would be to do:
406 : * conver_to_string_ex(arg);
407 : * append it and kill the now tmp arg.
408 : * but that could cause some E_NOTICE and also damn long lines.
409 : */
410 :
411 191 : switch (Z_TYPE_PP(arg)) {
412 : case IS_NULL:
413 37 : TRACE_APPEND_STR("NULL, ");
414 37 : break;
415 : case IS_STRING: {
416 : int l_added;
417 56 : TRACE_APPEND_CHR('\'');
418 56 : if (Z_STRLEN_PP(arg) > 15) {
419 8 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
420 8 : TRACE_APPEND_STR("...', ");
421 8 : l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
422 : } else {
423 48 : l_added = Z_STRLEN_PP(arg);
424 48 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
425 48 : TRACE_APPEND_STR("', ");
426 48 : l_added += 3 + 1;
427 : }
428 625 : while (--l_added) {
429 513 : if ((unsigned char)(*str)[*len - l_added] < 32) {
430 0 : (*str)[*len - l_added] = '?';
431 : }
432 : }
433 56 : break;
434 : }
435 : case IS_UNICODE: {
436 : int l_added;
437 : TSRMLS_FETCH();
438 :
439 : /*
440 : * We do not want to apply current error mode here, since
441 : * zend_make_printable_zval() uses output encoding converter.
442 : * Temporarily set output encoding converter to escape offending
443 : * chars with \uXXXX notation.
444 : */
445 46 : zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
446 46 : TRACE_APPEND_CHR('\'');
447 46 : if (Z_USTRLEN_PP(arg) > 15) {
448 17 : TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
449 17 : TRACE_APPEND_STR("...', ");
450 17 : l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
451 : } else {
452 29 : l_added = Z_USTRLEN_PP(arg);
453 29 : TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
454 29 : TRACE_APPEND_STR("', ");
455 29 : l_added += 3 + 1;
456 : }
457 : /*
458 : * Reset output encoding converter error mode.
459 : */
460 46 : zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
461 687 : while (--l_added) {
462 595 : if ((unsigned char)(*str)[*len - l_added] < 32) {
463 0 : (*str)[*len - l_added] = '?';
464 : }
465 : }
466 46 : break;
467 : }
468 : case IS_BOOL:
469 1 : if (Z_LVAL_PP(arg)) {
470 1 : TRACE_APPEND_STR("true, ");
471 : } else {
472 0 : TRACE_APPEND_STR("false, ");
473 : }
474 1 : break;
475 : case IS_RESOURCE:
476 1 : TRACE_APPEND_STR("Resource id #");
477 : /* break; */
478 : case IS_LONG: {
479 17 : long lval = Z_LVAL_PP(arg);
480 : char s_tmp[MAX_LENGTH_OF_LONG + 1];
481 17 : int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
482 17 : TRACE_APPEND_STRL(s_tmp, l_tmp);
483 17 : TRACE_APPEND_STR(", ");
484 17 : break;
485 : }
486 : case IS_DOUBLE: {
487 0 : double dval = Z_DVAL_PP(arg);
488 : char *s_tmp;
489 : int l_tmp;
490 :
491 0 : s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
492 0 : l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
493 0 : TRACE_APPEND_STRL(s_tmp, l_tmp);
494 : /* %G already handles removing trailing zeros from the fractional part, yay */
495 0 : efree(s_tmp);
496 0 : TRACE_APPEND_STR(", ");
497 0 : break;
498 : }
499 : case IS_ARRAY:
500 26 : TRACE_APPEND_STR("Array, ");
501 26 : break;
502 : case IS_OBJECT: {
503 : zstr class_name;
504 : zend_uint class_name_len;
505 : int dup;
506 : zval tmp;
507 :
508 8 : TRACE_APPEND_STR("Object(");
509 :
510 8 : dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
511 :
512 8 : ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
513 8 : convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
514 8 : TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
515 8 : zval_dtor(&tmp);
516 :
517 8 : if(!dup) {
518 8 : efree(class_name.v);
519 : }
520 :
521 8 : TRACE_APPEND_STR("), ");
522 : break;
523 : }
524 : default:
525 : break;
526 : }
527 191 : return ZEND_HASH_APPLY_KEEP;
528 : }
529 : /* }}} */
530 :
531 : static int _build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
532 125 : {
533 : char *s_tmp, **str;
534 : int *len, *num;
535 : long line;
536 125 : HashTable *ht = Z_ARRVAL_PP(frame);
537 : zval **file, **tmp;
538 :
539 125 : str = va_arg(args, char**);
540 125 : len = va_arg(args, int*);
541 125 : num = va_arg(args, int*);
542 :
543 125 : s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
544 125 : sprintf(s_tmp, "#%d ", (*num)++);
545 125 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
546 125 : efree(s_tmp);
547 125 : if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
548 111 : if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
549 111 : line = Z_LVAL_PP(tmp);
550 : } else {
551 0 : line = 0;
552 : }
553 111 : TRACE_APPEND_ZVAL(*file);
554 111 : s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
555 111 : sprintf(s_tmp, "(%ld): ", line);
556 111 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
557 111 : efree(s_tmp);
558 : } else {
559 14 : TRACE_APPEND_STR("[internal function]: ");
560 : }
561 125 : TRACE_APPEND_KEY("class");
562 125 : TRACE_APPEND_KEY("type");
563 125 : TRACE_APPEND_KEY("function");
564 125 : TRACE_APPEND_CHR('(');
565 125 : if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
566 124 : int last_len = *len;
567 124 : zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)_build_trace_args, 2, str, len);
568 124 : if (last_len != *len) {
569 85 : *len -= 2; /* remove last ', ' */
570 : }
571 : }
572 125 : TRACE_APPEND_STR(")\n");
573 125 : return ZEND_HASH_APPLY_KEEP;
574 : }
575 : /* }}} */
576 :
577 : /* {{{ proto string Exception::getTraceAsString() U
578 : Obtain the backtrace for the exception as a string (instead of an array) */
579 : ZEND_METHOD(exception, getTraceAsString)
580 99 : {
581 : zval *trace;
582 : char *res, **str, *s_tmp;
583 99 : int res_len = 0, *len = &res_len, num = 0;
584 :
585 99 : DEFAULT_0_PARAMS;
586 :
587 98 : res = estrdup("");
588 98 : str = &res;
589 :
590 98 : trace = zend_read_property(default_exception_ce, getThis(), "trace", sizeof("trace")-1, 1 TSRMLS_CC);
591 98 : zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)_build_trace_string, 3, str, len, &num);
592 :
593 98 : s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
594 98 : sprintf(s_tmp, "#%d {main}", num);
595 98 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
596 98 : efree(s_tmp);
597 :
598 98 : res[res_len] = '\0';
599 98 : RETURN_STRINGL(res, res_len, 0);
600 : }
601 : /* }}} */
602 :
603 : /* {{{ proto string Exception::getPrevious()
604 : Return previous Exception or NULL. */
605 : ZEND_METHOD(exception, getPrevious)
606 4 : {
607 : zval *previous;
608 :
609 4 : DEFAULT_0_PARAMS;
610 :
611 4 : previous = zend_read_property(default_exception_ce, getThis(), "previous", sizeof("previous")-1, 1 TSRMLS_CC);
612 4 : RETURN_ZVAL(previous, 1, 0);
613 : }
614 :
615 : int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
616 1191589 : {
617 : va_list arg;
618 : int len;
619 :
620 1191589 : va_start(arg, format);
621 1191589 : len = zend_vspprintf(message, max_len, format, arg);
622 1191589 : va_end(arg);
623 1191589 : return len;
624 : }
625 : /* }}} */
626 :
627 : /* {{{ proto string Exception::__toString() U
628 : Obtain the string representation of the Exception object */
629 : ZEND_METHOD(exception, __toString)
630 92 : {
631 : zval message, file, line, *trace, *exception;
632 : char *str, *prev_str;
633 92 : int len = 0;
634 : zend_fcall_info fci;
635 : zval fname;
636 :
637 92 : DEFAULT_0_PARAMS;
638 :
639 91 : str = estrndup("", 0);
640 :
641 91 : exception = getThis();
642 91 : ZVAL_ASCII_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
643 :
644 278 : while (exception && Z_TYPE_P(exception) == IS_OBJECT) {
645 97 : prev_str = str;
646 97 : _default_exception_get_entry(exception, "message", sizeof("message")-1, &message TSRMLS_CC);
647 97 : _default_exception_get_entry(exception, "file", sizeof("file")-1, &file TSRMLS_CC);
648 97 : _default_exception_get_entry(exception, "line", sizeof("line")-1, &line TSRMLS_CC);
649 :
650 97 : convert_to_unicode(&message);
651 96 : convert_to_unicode(&file);
652 96 : convert_to_long(&line);
653 :
654 96 : fci.size = sizeof(fci);
655 96 : fci.function_table = &Z_OBJCE_P(getThis())->function_table;
656 96 : fci.function_name = &fname;
657 96 : fci.symbol_table = NULL;
658 96 : fci.object_ptr = exception;
659 96 : fci.retval_ptr_ptr = &trace;
660 96 : fci.param_count = 0;
661 96 : fci.params = NULL;
662 96 : fci.no_separation = 1;
663 :
664 96 : zend_call_function(&fci, NULL TSRMLS_CC);
665 :
666 96 : if (Z_TYPE_P(trace) != IS_STRING && Z_TYPE_P(trace) != IS_UNICODE) {
667 0 : trace = NULL;
668 : }
669 :
670 96 : if (Z_UNILEN(message) > 0) {
671 88 : len = zend_spprintf(&str, 0, "exception '%v' with message '%R' in %R:%ld\nStack trace:\n%s%s%s",
672 : Z_OBJCE_P(exception)->name, Z_TYPE(message), Z_UNIVAL(message), Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
673 : (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
674 : len ? "\n\nNext " : "", prev_str);
675 : } else {
676 8 : len = zend_spprintf(&str, 0, "exception '%v' in %R:%ld\nStack trace:\n%s%s%s",
677 : Z_OBJCE_P(exception)->name, Z_TYPE(file), Z_UNIVAL(file), Z_LVAL(line),
678 : (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
679 : len ? "\n\nNext " : "", prev_str);
680 : }
681 96 : efree(prev_str);
682 96 : zval_dtor(&message);
683 96 : zval_dtor(&file);
684 96 : zval_dtor(&line);
685 :
686 96 : exception = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 0 TSRMLS_CC);
687 : }
688 90 : zval_dtor(&fname);
689 :
690 : /* We store the result in the private property string so we can access
691 : * the result in uncaught exception handlers without memleaks. */
692 90 : zend_update_property_string(default_exception_ce, getThis(), "string", sizeof("string")-1, str TSRMLS_CC);
693 :
694 90 : if (trace) {
695 90 : zval_ptr_dtor(&trace);
696 : }
697 :
698 90 : RETURN_STRINGL(str, len, 0);
699 : }
700 : /* }}} */
701 :
702 : /* {{{ internals structs */
703 : /* All functions that may be used in uncaught exception handlers must be final
704 : * and must not throw exceptions. Otherwise we would need a facility to handle
705 : * such exceptions in that handler.
706 : * Also all getXY() methods are final because thy serve as read only access to
707 : * their corresponding properties, no more, no less. If after all you need to
708 : * override somthing then it is method __toString().
709 : * And never try to change the state of exceptions and never implement anything
710 : * that gives the user anything to accomplish this.
711 : */
712 : ZEND_BEGIN_ARG_INFO_EX(arginfo_exception___construct, 0, 0, 0)
713 : ZEND_ARG_INFO(0, message)
714 : ZEND_ARG_INFO(0, code)
715 : ZEND_ARG_INFO(0, previous)
716 : ZEND_END_ARG_INFO()
717 :
718 : static const zend_function_entry default_exception_functions[] = {
719 : ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
720 : ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
721 : ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
722 : ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
723 : ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
724 : ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
725 : ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
726 : ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
727 : ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
728 : ZEND_ME(exception, __toString, NULL, 0)
729 : {NULL, NULL, NULL}
730 : };
731 :
732 : ZEND_BEGIN_ARG_INFO_EX(arginfo_error_exception___construct, 0, 0, 0)
733 : ZEND_ARG_INFO(0, message)
734 : ZEND_ARG_INFO(0, code)
735 : ZEND_ARG_INFO(0, severity)
736 : ZEND_ARG_INFO(0, filename)
737 : ZEND_ARG_INFO(0, lineno)
738 : ZEND_ARG_INFO(0, previous)
739 : ZEND_END_ARG_INFO()
740 :
741 : static const zend_function_entry error_exception_functions[] = {
742 : ZEND_ME(error_exception, __construct, arginfo_error_exception___construct, ZEND_ACC_PUBLIC)
743 : ZEND_ME(error_exception, getSeverity, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
744 : {NULL, NULL, NULL}
745 : };
746 : /* }}} */
747 :
748 : void zend_register_default_exception(TSRMLS_D) /* {{{ */
749 17007 : {
750 : zend_class_entry ce;
751 :
752 17007 : INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
753 17007 : default_exception_ce = zend_register_internal_class(&ce TSRMLS_CC);
754 17007 : default_exception_ce->create_object = zend_default_exception_new;
755 17007 : memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
756 17007 : default_exception_handlers.clone_obj = NULL;
757 :
758 17007 : zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC);
759 17007 : zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC);
760 17007 : zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED TSRMLS_CC);
761 17007 : zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
762 17007 : zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
763 17007 : zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
764 17007 : zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
765 :
766 17007 : INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
767 17007 : error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce, NULL TSRMLS_CC);
768 17007 : error_exception_ce->create_object = zend_error_exception_new;
769 17007 : zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED TSRMLS_CC);
770 17007 : }
771 : /* }}} */
772 :
773 : ZEND_API zend_class_entry *zend_exception_get_default(TSRMLS_D) /* {{{ */
774 119509 : {
775 119509 : return default_exception_ce;
776 : }
777 : /* }}} */
778 :
779 : ZEND_API zend_class_entry *zend_get_error_exception(TSRMLS_D) /* {{{ */
780 0 : {
781 0 : return error_exception_ce;
782 : }
783 : /* }}} */
784 :
785 : ZEND_API zval * zend_throw_exception(zend_class_entry *exception_ce, char *message, long code TSRMLS_DC) /* {{{ */
786 835 : {
787 : zval *ex;
788 :
789 835 : MAKE_STD_ZVAL(ex);
790 835 : if (exception_ce) {
791 728 : if (!instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
792 0 : zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class");
793 0 : exception_ce = default_exception_ce;
794 : }
795 : } else {
796 107 : exception_ce = default_exception_ce;
797 : }
798 835 : object_init_ex(ex, exception_ce);
799 :
800 :
801 835 : if (message) {
802 835 : zend_update_property_rt_string(default_exception_ce, ex, "message", sizeof("message")-1, message TSRMLS_CC);
803 : }
804 835 : if (code) {
805 59 : zend_update_property_long(default_exception_ce, ex, "code", sizeof("code")-1, code TSRMLS_CC);
806 : }
807 :
808 835 : zend_throw_exception_internal(ex TSRMLS_CC);
809 835 : return ex;
810 : }
811 : /* }}} */
812 :
813 : ZEND_API zval * zend_throw_exception_ex(zend_class_entry *exception_ce, long code TSRMLS_DC, char *format, ...) /* {{{ */
814 564 : {
815 : va_list arg;
816 : char *message;
817 : zval *zexception;
818 :
819 564 : va_start(arg, format);
820 564 : zend_vspprintf(&message, 0, format, arg);
821 564 : va_end(arg);
822 564 : zexception = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
823 564 : efree(message);
824 564 : return zexception;
825 : }
826 : /* }}} */
827 :
828 : ZEND_API zval * zend_throw_error_exception(zend_class_entry *exception_ce, char *message, long code, int severity TSRMLS_DC) /* {{{ */
829 131 : {
830 131 : zval *ex = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
831 131 : zend_update_property_long(default_exception_ce, ex, "severity", sizeof("severity")-1, severity TSRMLS_CC);
832 131 : return ex;
833 : }
834 : /* }}} */
835 :
836 : static void zend_error_va(int type, const char *file, uint lineno, const char *format, ...) /* {{{ */
837 82 : {
838 : va_list args;
839 :
840 82 : va_start(args, format);
841 82 : zend_error_cb(type, file, lineno, format, args);
842 0 : va_end(args);
843 0 : }
844 : /* }}} */
845 :
846 : /* This function doesn't return if it uses E_ERROR */
847 : ZEND_API void zend_exception_error(zval *exception, int severity TSRMLS_DC) /* {{{ */
848 83 : {
849 83 : zend_class_entry *ce_exception = Z_OBJCE_P(exception);
850 83 : if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
851 : zval *str, *file, *line;
852 :
853 83 : EG(exception) = NULL;
854 :
855 83 : zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", &str);
856 82 : if (!EG(exception)) {
857 82 : if (Z_TYPE_P(str) == IS_UNICODE) {
858 0 : zend_update_property_unicodel(default_exception_ce, exception, "string", sizeof("string")-1, Z_USTRVAL_P(str), Z_USTRLEN_P(str) TSRMLS_CC);
859 82 : } else if (Z_TYPE_P(str) == IS_STRING) {
860 82 : zend_update_property_stringl(default_exception_ce, exception, "string", sizeof("string")-1, Z_STRVAL_P(str), Z_STRLEN_P(str) TSRMLS_CC);
861 : } else {
862 0 : zend_error(E_WARNING, "%v::__toString() must return a string", ce_exception->name);
863 : }
864 : }
865 82 : zval_ptr_dtor(&str);
866 :
867 82 : if (EG(exception)) {
868 : /* do the best we can to inform about the inner exception */
869 0 : if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
870 0 : file = zend_read_property(default_exception_ce, EG(exception), "file", sizeof("file")-1, 1 TSRMLS_CC);
871 0 : line = zend_read_property(default_exception_ce, EG(exception), "line", sizeof("line")-1, 1 TSRMLS_CC);
872 : } else {
873 0 : file = NULL;
874 0 : line = NULL;
875 : }
876 0 : zend_error_va(E_WARNING, file ? Z_STRVAL_P(file) : NULL, line ? Z_LVAL_P(line) : 0, "Uncaught %v in exception handling during call to %v::__tostring()", Z_OBJCE_P(EG(exception))->name, ce_exception->name);
877 : }
878 :
879 82 : str = zend_read_property(default_exception_ce, exception, "string", sizeof("string")-1, 1 TSRMLS_CC);
880 82 : file = zend_read_property(default_exception_ce, exception, "file", sizeof("file")-1, 1 TSRMLS_CC);
881 82 : line = zend_read_property(default_exception_ce, exception, "line", sizeof("line")-1, 1 TSRMLS_CC);
882 :
883 82 : if (Z_TYPE_P(file) == IS_UNICODE) {
884 : zval copy;
885 : int use_copy;
886 81 : zend_make_printable_zval(file, ©, &use_copy);
887 81 : zend_error_va(E_ERROR, Z_STRVAL(copy), Z_LVAL_P(line), "Uncaught %R\n thrown", Z_TYPE_P(str), Z_UNIVAL_P(str));
888 0 : zval_dtor(©);
889 : } else {
890 1 : zend_error_va(severity, Z_STRVAL_P(file), Z_LVAL_P(line), "Uncaught %R\n thrown", Z_TYPE_P(str), Z_UNIVAL_P(str));
891 : }
892 : } else {
893 0 : zend_error(severity, "Uncaught exception '%v'", ce_exception->name);
894 : }
895 0 : }
896 : /* }}} */
897 :
898 : ZEND_API void zend_throw_exception_object(zval *exception TSRMLS_DC) /* {{{ */
899 246 : {
900 : zend_class_entry *exception_ce;
901 :
902 246 : if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
903 0 : zend_error(E_ERROR, "Need to supply an object when throwing an exception");
904 : }
905 :
906 246 : exception_ce = Z_OBJCE_P(exception);
907 :
908 246 : if (!exception_ce || !instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
909 1 : zend_error(E_ERROR, "Exceptions must be valid objects derived from the Exception base class");
910 : }
911 245 : zend_throw_exception_internal(exception TSRMLS_CC);
912 245 : }
913 : /* }}} */
914 :
915 : /*
916 : * Local variables:
917 : * tab-width: 4
918 : * c-basic-offset: 4
919 : * indent-tabs-mode: t
920 : * End:
921 : */
|