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