Annotation of embedaddon/php/Zend/zend_exceptions.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | Zend Engine                                                          |
                      4:    +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:    | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) |
1.1       misho       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: 
1.1.1.2   misho      22: /* $Id$ */
1.1       misho      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"
1.1.1.2   misho      30: #include "zend_dtrace.h"
1.1       misho      31: 
1.1.1.3 ! misho      32: static zend_class_entry *default_exception_ce;
        !            33: static zend_class_entry *error_exception_ce;
1.1       misho      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: {
                     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: 
                     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: 
                     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: 
                     84: void zend_throw_exception_internal(zval *exception TSRMLS_DC) /* {{{ */
                     85: {
1.1.1.2   misho      86: #ifdef HAVE_DTRACE
                     87:        if (DTRACE_EXCEPTION_THROWN_ENABLED()) {
                     88:                char *classname;
                     89:                int name_len;
                     90: 
                     91:                if (exception != NULL) {
                     92:                        zend_get_object_classname(exception, &classname, &name_len TSRMLS_CC);
                     93:                        DTRACE_EXCEPTION_THROWN(classname);
                     94:                } else {
                     95:                        DTRACE_EXCEPTION_THROWN(NULL);
                     96:                }
                     97:        }
                     98: #endif /* HAVE_DTRACE */
                     99: 
1.1       misho     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: 
                    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: 
                    147: static zend_object_value zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces TSRMLS_DC) /* {{{ */
                    148: {
1.1.1.2   misho     149:        zval obj;
1.1       misho     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: 
1.1.1.2   misho     156:        object_properties_init(object, class_type);
1.1       misho     157: 
                    158:        ALLOC_ZVAL(trace);
                    159:        Z_UNSET_ISREF_P(trace);
                    160:        Z_SET_REFCOUNT_P(trace, 0);
1.1.1.2   misho     161:        zend_fetch_debug_backtrace(trace, skip_top_traces, 0, 0 TSRMLS_CC);
1.1       misho     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: 
                    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: 
                    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 */
                    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 */
                    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) {
1.1.1.2   misho     208:                zend_update_property_stringl(default_exception_ce, object, "message", sizeof("message")-1, message, message_len TSRMLS_CC);
1.1       misho     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 ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Exception previous]]])
                    222:    ErrorException constructor */
                    223: ZEND_METHOD(error_exception, __construct)
                    224: {
                    225:        char  *message = NULL, *filename = NULL;
                    226:        long   code = 0, severity = E_ERROR, lineno;
                    227:        zval  *object, *previous = NULL;
                    228:        int    argc = ZEND_NUM_ARGS(), message_len, filename_len;
                    229: 
                    230:        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) {
                    231:                zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno  [, Exception $previous = NULL]]]]]])");
                    232:        }
                    233: 
                    234:        object = getThis();
                    235: 
                    236:        if (message) {
                    237:                zend_update_property_string(default_exception_ce, object, "message", sizeof("message")-1, message TSRMLS_CC);
                    238:        }
                    239: 
                    240:        if (code) {
                    241:                zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
                    242:        }
                    243: 
                    244:        if (previous) {
                    245:                zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
                    246:        }
                    247: 
                    248:        zend_update_property_long(default_exception_ce, object, "severity", sizeof("severity")-1, severity TSRMLS_CC);
                    249: 
                    250:        if (argc >= 4) {
                    251:            zend_update_property_string(default_exception_ce, object, "file", sizeof("file")-1, filename TSRMLS_CC);
                    252:        if (argc < 5) {
                    253:            lineno = 0; /* invalidate lineno */
                    254:        }
                    255:        zend_update_property_long(default_exception_ce, object, "line", sizeof("line")-1, lineno TSRMLS_CC);
                    256:        }
                    257: }
                    258: /* }}} */
                    259: 
                    260: #define DEFAULT_0_PARAMS \
                    261:        if (zend_parse_parameters_none() == FAILURE) { \
                    262:                return; \
                    263:        }
                    264: 
                    265: static void _default_exception_get_entry(zval *object, char *name, int name_len, zval *return_value TSRMLS_DC) /* {{{ */
                    266: {
                    267:        zval *value;
                    268: 
                    269:        value = zend_read_property(default_exception_ce, object, name, name_len, 0 TSRMLS_CC);
                    270: 
                    271:        *return_value = *value;
                    272:        zval_copy_ctor(return_value);
                    273:        INIT_PZVAL(return_value);
                    274: }
                    275: /* }}} */
                    276: 
                    277: /* {{{ proto string Exception::getFile()
                    278:    Get the file in which the exception occurred */
                    279: ZEND_METHOD(exception, getFile)
                    280: {
                    281:        DEFAULT_0_PARAMS;
                    282: 
                    283:        _default_exception_get_entry(getThis(), "file", sizeof("file")-1, return_value TSRMLS_CC);
                    284: }
                    285: /* }}} */
                    286: 
                    287: /* {{{ proto int Exception::getLine()
                    288:    Get the line in which the exception occurred */
                    289: ZEND_METHOD(exception, getLine)
                    290: {
                    291:        DEFAULT_0_PARAMS;
                    292: 
                    293:        _default_exception_get_entry(getThis(), "line", sizeof("line")-1, return_value TSRMLS_CC);
                    294: }
                    295: /* }}} */
                    296: 
                    297: /* {{{ proto string Exception::getMessage()
                    298:    Get the exception message */
                    299: ZEND_METHOD(exception, getMessage)
                    300: {
                    301:        DEFAULT_0_PARAMS;
                    302: 
                    303:        _default_exception_get_entry(getThis(), "message", sizeof("message")-1, return_value TSRMLS_CC);
                    304: }
                    305: /* }}} */
                    306: 
                    307: /* {{{ proto int Exception::getCode()
                    308:    Get the exception code */
                    309: ZEND_METHOD(exception, getCode)
                    310: {
                    311:        DEFAULT_0_PARAMS;
                    312: 
                    313:        _default_exception_get_entry(getThis(), "code", sizeof("code")-1, return_value TSRMLS_CC);
                    314: }
                    315: /* }}} */
                    316: 
                    317: /* {{{ proto array Exception::getTrace()
                    318:    Get the stack trace for the location in which the exception occurred */
                    319: ZEND_METHOD(exception, getTrace)
                    320: {
                    321:        DEFAULT_0_PARAMS;
                    322: 
                    323:        _default_exception_get_entry(getThis(), "trace", sizeof("trace")-1, return_value TSRMLS_CC);
                    324: }
                    325: /* }}} */
                    326: 
                    327: /* {{{ proto int ErrorException::getSeverity()
                    328:    Get the exception severity */
                    329: ZEND_METHOD(error_exception, getSeverity)
                    330: {
                    331:        DEFAULT_0_PARAMS;
                    332: 
                    333:        _default_exception_get_entry(getThis(), "severity", sizeof("severity")-1, return_value TSRMLS_CC);
                    334: }
                    335: /* }}} */
                    336: 
                    337: /* {{{ gettraceasstring() macros */
                    338: #define TRACE_APPEND_CHR(chr)                                            \
                    339:        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
                    340:        (*str)[(*len)++] = chr
                    341: 
                    342: #define TRACE_APPEND_STRL(val, vallen)                                   \
                    343:        {                                                                    \
                    344:                int l = vallen;                                                  \
                    345:                *str = (char*)erealloc(*str, *len + l + 1);                      \
                    346:                memcpy((*str) + *len, val, l);                                   \
                    347:                *len += l;                                                       \
                    348:        }
                    349: 
                    350: #define TRACE_APPEND_STR(val)                                            \
                    351:        TRACE_APPEND_STRL(val, sizeof(val)-1)
                    352: 
1.1.1.3 ! misho     353: #define TRACE_APPEND_KEY(key)                                                   \
        !           354:        if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) {    \
        !           355:                if (Z_TYPE_PP(tmp) != IS_STRING) {                              \
        !           356:                        zend_error(E_WARNING, "Value for %s is no string", key); \
        !           357:                        TRACE_APPEND_STR("[unknown]");                          \
        !           358:                } else {                                                        \
        !           359:                        TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));  \
        !           360:                }                                                               \
1.1       misho     361:        }
                    362: 
                    363: /* }}} */
                    364: 
                    365: static int _build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                    366: {
                    367:        char **str;
                    368:        int *len;
                    369: 
                    370:        str = va_arg(args, char**);
                    371:        len = va_arg(args, int*);
                    372: 
                    373:        /* the trivial way would be to do:
                    374:         * conver_to_string_ex(arg);
                    375:         * append it and kill the now tmp arg.
                    376:         * but that could cause some E_NOTICE and also damn long lines.
                    377:         */
                    378: 
                    379:        switch (Z_TYPE_PP(arg)) {
                    380:                case IS_NULL:
                    381:                        TRACE_APPEND_STR("NULL, ");
                    382:                        break;
                    383:                case IS_STRING: {
                    384:                        int l_added;
                    385:                        TRACE_APPEND_CHR('\'');
                    386:                        if (Z_STRLEN_PP(arg) > 15) {
                    387:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                    388:                                TRACE_APPEND_STR("...', ");
                    389:                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                    390:                        } else {
                    391:                                l_added = Z_STRLEN_PP(arg);
                    392:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                    393:                                TRACE_APPEND_STR("', ");
                    394:                                l_added += 3 + 1;
                    395:                        }
                    396:                        while (--l_added) {
                    397:                                if ((*str)[*len - l_added] < 32) {
                    398:                                        (*str)[*len - l_added] = '?';
                    399:                                }
                    400:                        }
                    401:                        break;
                    402:                }
                    403:                case IS_BOOL:
                    404:                        if (Z_LVAL_PP(arg)) {
                    405:                                TRACE_APPEND_STR("true, ");
                    406:                        } else {
                    407:                                TRACE_APPEND_STR("false, ");
                    408:                        }
                    409:                        break;
                    410:                case IS_RESOURCE:
                    411:                        TRACE_APPEND_STR("Resource id #");
                    412:                        /* break; */
                    413:                case IS_LONG: {
                    414:                        long lval = Z_LVAL_PP(arg);
                    415:                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                    416:                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                    417:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                    418:                        TRACE_APPEND_STR(", ");
                    419:                        break;
                    420:                }
                    421:                case IS_DOUBLE: {
                    422:                        double dval = Z_DVAL_PP(arg);
                    423:                        char *s_tmp;
                    424:                        int l_tmp;
                    425: 
                    426:                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                    427:                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                    428:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                    429:                        /* %G already handles removing trailing zeros from the fractional part, yay */
                    430:                        efree(s_tmp);
                    431:                        TRACE_APPEND_STR(", ");
                    432:                        break;
                    433:                }
                    434:                case IS_ARRAY:
                    435:                        TRACE_APPEND_STR("Array, ");
                    436:                        break;
                    437:                case IS_OBJECT: {
1.1.1.2   misho     438:                        const char *class_name;
1.1       misho     439:                        zend_uint class_name_len;
                    440:                        int dup;
                    441: 
                    442:                        TRACE_APPEND_STR("Object(");
                    443: 
                    444:                        dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
                    445: 
                    446:                        TRACE_APPEND_STRL(class_name, class_name_len);
                    447:                        if(!dup) {
1.1.1.2   misho     448:                                efree((char*)class_name);
1.1       misho     449:                        }
                    450: 
                    451:                        TRACE_APPEND_STR("), ");
                    452:                        break;
                    453:                }
                    454:                default:
                    455:                        break;
                    456:        }
                    457:        return ZEND_HASH_APPLY_KEEP;
                    458: }
                    459: /* }}} */
                    460: 
                    461: static int _build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                    462: {
                    463:        char *s_tmp, **str;
                    464:        int *len, *num;
                    465:        long line;
                    466:        HashTable *ht = Z_ARRVAL_PP(frame);
                    467:        zval **file, **tmp;
                    468: 
1.1.1.3 ! misho     469:        if (Z_TYPE_PP(frame) != IS_ARRAY) {
        !           470:                zend_error(E_WARNING, "Expected array for frame %lu", hash_key->h);
        !           471:                return ZEND_HASH_APPLY_KEEP;
        !           472:        }
        !           473: 
1.1       misho     474:        str = va_arg(args, char**);
                    475:        len = va_arg(args, int*);
                    476:        num = va_arg(args, int*);
                    477: 
                    478:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
                    479:        sprintf(s_tmp, "#%d ", (*num)++);
                    480:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                    481:        efree(s_tmp);
                    482:        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1.1.1.3 ! misho     483:                if (Z_TYPE_PP(file) != IS_STRING) {
        !           484:                        zend_error(E_WARNING, "Function name is no string");
        !           485:                        TRACE_APPEND_STR("[unknown function]");
        !           486:                } else{
        !           487:                        if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
        !           488:                                if (Z_TYPE_PP(tmp) == IS_LONG) {
        !           489:                                        line = Z_LVAL_PP(tmp);
        !           490:                                } else {
        !           491:                                        zend_error(E_WARNING, "Line is no long");
        !           492:                                        line = 0;
        !           493:                                }
        !           494:                        } else {
        !           495:                                line = 0;
        !           496:                        }
        !           497:                        s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
        !           498:                        sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
        !           499:                        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
        !           500:                        efree(s_tmp);
1.1       misho     501:                }
                    502:        } else {
                    503:                TRACE_APPEND_STR("[internal function]: ");
                    504:        }
                    505:        TRACE_APPEND_KEY("class");
                    506:        TRACE_APPEND_KEY("type");
                    507:        TRACE_APPEND_KEY("function");
                    508:        TRACE_APPEND_CHR('(');
                    509:        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1.1.1.3 ! misho     510:                if (Z_TYPE_PP(tmp) == IS_ARRAY) {
        !           511:                        int last_len = *len;
        !           512:                        zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)_build_trace_args, 2, str, len);
        !           513:                        if (last_len != *len) {
        !           514:                                *len -= 2; /* remove last ', ' */
        !           515:                        }
        !           516:                } else {
        !           517:                        zend_error(E_WARNING, "args element is no array");
1.1       misho     518:                }
                    519:        }
                    520:        TRACE_APPEND_STR(")\n");
                    521:        return ZEND_HASH_APPLY_KEEP;
                    522: }
                    523: /* }}} */
                    524: 
                    525: /* {{{ proto string Exception::getTraceAsString()
                    526:    Obtain the backtrace for the exception as a string (instead of an array) */
                    527: ZEND_METHOD(exception, getTraceAsString)
                    528: {
                    529:        zval *trace;
                    530:        char *res, **str, *s_tmp;
                    531:        int res_len = 0, *len = &res_len, num = 0;
                    532: 
                    533:        DEFAULT_0_PARAMS;
                    534:        
                    535:        res = estrdup("");
                    536:        str = &res;
                    537: 
                    538:        trace = zend_read_property(default_exception_ce, getThis(), "trace", sizeof("trace")-1, 1 TSRMLS_CC);
                    539:        zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)_build_trace_string, 3, str, len, &num);
                    540: 
                    541:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
                    542:        sprintf(s_tmp, "#%d {main}", num);
                    543:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                    544:        efree(s_tmp);
                    545: 
                    546:        res[res_len] = '\0';    
                    547:        RETURN_STRINGL(res, res_len, 0); 
                    548: }
                    549: /* }}} */
                    550: 
                    551: /* {{{ proto string Exception::getPrevious()
                    552:    Return previous Exception or NULL. */
                    553: ZEND_METHOD(exception, getPrevious)
                    554: {
                    555:        zval *previous;
                    556: 
                    557:        DEFAULT_0_PARAMS;
                    558: 
                    559:        previous = zend_read_property(default_exception_ce, getThis(), "previous", sizeof("previous")-1, 1 TSRMLS_CC);
                    560:        RETURN_ZVAL(previous, 1, 0);
                    561: }
                    562: 
                    563: int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
                    564: {
                    565:        va_list arg;
                    566:        int len;
                    567: 
                    568:        va_start(arg, format);
                    569:        len = zend_vspprintf(message, max_len, format, arg);
                    570:        va_end(arg);
                    571:        return len;
                    572: }
                    573: /* }}} */
                    574: 
                    575: /* {{{ proto string Exception::__toString()
                    576:    Obtain the string representation of the Exception object */
                    577: ZEND_METHOD(exception, __toString)
                    578: {
                    579:        zval message, file, line, *trace, *exception;
                    580:        char *str, *prev_str;
                    581:        int len = 0;
                    582:        zend_fcall_info fci;
                    583:        zval fname;
                    584:        
                    585:        DEFAULT_0_PARAMS;
                    586:        
                    587:        str = estrndup("", 0);
                    588: 
                    589:        exception = getThis();
                    590:        ZVAL_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
                    591: 
                    592:        while (exception && Z_TYPE_P(exception) == IS_OBJECT) {
                    593:                prev_str = str;
                    594:                _default_exception_get_entry(exception, "message", sizeof("message")-1, &message TSRMLS_CC);
                    595:                _default_exception_get_entry(exception, "file", sizeof("file")-1, &file TSRMLS_CC);
                    596:                _default_exception_get_entry(exception, "line", sizeof("line")-1, &line TSRMLS_CC);
                    597: 
                    598:                convert_to_string(&message);
                    599:                convert_to_string(&file);
                    600:                convert_to_long(&line);
                    601: 
                    602:                fci.size = sizeof(fci);
                    603:                fci.function_table = &Z_OBJCE_P(exception)->function_table;
                    604:                fci.function_name = &fname;
                    605:                fci.symbol_table = NULL;
                    606:                fci.object_ptr = exception;
                    607:                fci.retval_ptr_ptr = &trace;
                    608:                fci.param_count = 0;
                    609:                fci.params = NULL;
                    610:                fci.no_separation = 1;
                    611: 
                    612:                zend_call_function(&fci, NULL TSRMLS_CC);
                    613: 
                    614:                if (Z_TYPE_P(trace) != IS_STRING) {
                    615:                        zval_ptr_dtor(&trace);
                    616:                        trace = NULL;
                    617:                }
                    618: 
                    619:                if (Z_STRLEN(message) > 0) {
                    620:                        len = zend_spprintf(&str, 0, "exception '%s' with message '%s' in %s:%ld\nStack trace:\n%s%s%s",
                    621:                                                                Z_OBJCE_P(exception)->name, Z_STRVAL(message), Z_STRVAL(file), Z_LVAL(line),
                    622:                                                                (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
                    623:                                                                len ? "\n\nNext " : "", prev_str);
                    624:                } else {
                    625:                        len = zend_spprintf(&str, 0, "exception '%s' in %s:%ld\nStack trace:\n%s%s%s",
                    626:                                                                Z_OBJCE_P(exception)->name, Z_STRVAL(file), Z_LVAL(line),
                    627:                                                                (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
                    628:                                                                len ? "\n\nNext " : "", prev_str);
                    629:                }
                    630:                efree(prev_str);
                    631:                zval_dtor(&message);
                    632:                zval_dtor(&file);
                    633:                zval_dtor(&line);
                    634: 
                    635:                exception = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 0 TSRMLS_CC);
                    636: 
                    637:                if (trace) {
                    638:                        zval_ptr_dtor(&trace);
                    639:                }
1.1.1.2   misho     640: 
1.1       misho     641:        }
                    642:        zval_dtor(&fname);
                    643: 
                    644:        /* We store the result in the private property string so we can access
                    645:         * the result in uncaught exception handlers without memleaks. */
                    646:        zend_update_property_string(default_exception_ce, getThis(), "string", sizeof("string")-1, str TSRMLS_CC);
                    647: 
                    648:        RETURN_STRINGL(str, len, 0);
                    649: }
                    650: /* }}} */
                    651: 
                    652: /* {{{ internal structs */
                    653: /* All functions that may be used in uncaught exception handlers must be final
                    654:  * and must not throw exceptions. Otherwise we would need a facility to handle
                    655:  * such exceptions in that handler.
                    656:  * Also all getXY() methods are final because thy serve as read only access to
                    657:  * their corresponding properties, no more, no less. If after all you need to
                    658:  * override somthing then it is method __toString().
                    659:  * And never try to change the state of exceptions and never implement anything
                    660:  * that gives the user anything to accomplish this.
                    661:  */
                    662: ZEND_BEGIN_ARG_INFO_EX(arginfo_exception___construct, 0, 0, 0)
                    663:        ZEND_ARG_INFO(0, message)
                    664:        ZEND_ARG_INFO(0, code)
                    665:        ZEND_ARG_INFO(0, previous)
                    666: ZEND_END_ARG_INFO()
                    667: 
                    668: const static zend_function_entry default_exception_functions[] = {
                    669:        ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
                    670:        ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
                    671:        ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    672:        ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    673:        ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    674:        ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    675:        ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    676:        ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    677:        ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    678:        ZEND_ME(exception, __toString, NULL, 0)
                    679:        {NULL, NULL, NULL}
                    680: };
                    681: 
                    682: ZEND_BEGIN_ARG_INFO_EX(arginfo_error_exception___construct, 0, 0, 0)
                    683:        ZEND_ARG_INFO(0, message)
                    684:        ZEND_ARG_INFO(0, code)
                    685:        ZEND_ARG_INFO(0, severity)
                    686:        ZEND_ARG_INFO(0, filename)
                    687:        ZEND_ARG_INFO(0, lineno)
                    688:        ZEND_ARG_INFO(0, previous)
                    689: ZEND_END_ARG_INFO()
                    690: 
                    691: static const zend_function_entry error_exception_functions[] = {
                    692:        ZEND_ME(error_exception, __construct, arginfo_error_exception___construct, ZEND_ACC_PUBLIC)
                    693:        ZEND_ME(error_exception, getSeverity, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    694:        {NULL, NULL, NULL}
                    695: };
                    696: /* }}} */
                    697: 
                    698: void zend_register_default_exception(TSRMLS_D) /* {{{ */
                    699: {
                    700:        zend_class_entry ce;
                    701: 
                    702:        INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
                    703:        default_exception_ce = zend_register_internal_class(&ce TSRMLS_CC);
                    704:        default_exception_ce->create_object = zend_default_exception_new;
                    705:        memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
                    706:        default_exception_handlers.clone_obj = NULL;
                    707: 
                    708:        zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC);
                    709:        zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC);
                    710:        zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED TSRMLS_CC);
                    711:        zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
                    712:        zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
                    713:        zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
                    714:        zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
                    715: 
                    716:        INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
                    717:        error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce, NULL TSRMLS_CC);
                    718:        error_exception_ce->create_object = zend_error_exception_new;
                    719:        zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED TSRMLS_CC);
                    720: }
                    721: /* }}} */
                    722: 
                    723: ZEND_API zend_class_entry *zend_exception_get_default(TSRMLS_D) /* {{{ */
                    724: {
                    725:        return default_exception_ce;
                    726: }
                    727: /* }}} */
                    728: 
                    729: ZEND_API zend_class_entry *zend_get_error_exception(TSRMLS_D) /* {{{ */
                    730: {
                    731:        return error_exception_ce;
                    732: }
                    733: /* }}} */
                    734: 
                    735: ZEND_API zval * zend_throw_exception(zend_class_entry *exception_ce, char *message, long code TSRMLS_DC) /* {{{ */
                    736: {
                    737:        zval *ex;
                    738: 
                    739:        MAKE_STD_ZVAL(ex);
                    740:        if (exception_ce) {
                    741:                if (!instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
                    742:                        zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class");
                    743:                        exception_ce = default_exception_ce;
                    744:                }
                    745:        } else {
                    746:                exception_ce = default_exception_ce;
                    747:        }
                    748:        object_init_ex(ex, exception_ce);
                    749: 
                    750: 
                    751:        if (message) {
                    752:                zend_update_property_string(default_exception_ce, ex, "message", sizeof("message")-1, message TSRMLS_CC);
                    753:        }
                    754:        if (code) {
                    755:                zend_update_property_long(default_exception_ce, ex, "code", sizeof("code")-1, code TSRMLS_CC);
                    756:        }
                    757: 
                    758:        zend_throw_exception_internal(ex TSRMLS_CC);
                    759:        return ex;
                    760: }
                    761: /* }}} */
                    762: 
                    763: ZEND_API zval * zend_throw_exception_ex(zend_class_entry *exception_ce, long code TSRMLS_DC, char *format, ...) /* {{{ */
                    764: {
                    765:        va_list arg;
                    766:        char *message;
                    767:        zval *zexception;
                    768: 
                    769:        va_start(arg, format);
                    770:        zend_vspprintf(&message, 0, format, arg);
                    771:        va_end(arg);
                    772:        zexception = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
                    773:        efree(message);
                    774:        return zexception;
                    775: }
                    776: /* }}} */
                    777: 
                    778: ZEND_API zval * zend_throw_error_exception(zend_class_entry *exception_ce, char *message, long code, int severity TSRMLS_DC) /* {{{ */
                    779: {
                    780:        zval *ex = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
                    781:        zend_update_property_long(default_exception_ce, ex, "severity", sizeof("severity")-1, severity TSRMLS_CC);
                    782:        return ex;
                    783: }
                    784: /* }}} */
                    785: 
                    786: static void zend_error_va(int type, const char *file, uint lineno, const char *format, ...) /* {{{ */
                    787: {
                    788:        va_list args;
                    789: 
                    790:        va_start(args, format);
                    791:        zend_error_cb(type, file, lineno, format, args);
                    792:        va_end(args);
                    793: }
                    794: /* }}} */
                    795: 
                    796: /* This function doesn't return if it uses E_ERROR */
                    797: ZEND_API void zend_exception_error(zval *exception, int severity TSRMLS_DC) /* {{{ */
                    798: {
                    799:        zend_class_entry *ce_exception = Z_OBJCE_P(exception);
                    800:        if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
                    801:                zval *str, *file, *line;
                    802: 
                    803:                EG(exception) = NULL;
                    804: 
                    805:                zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", &str);
                    806:                if (!EG(exception)) {
                    807:                        if (Z_TYPE_P(str) != IS_STRING) {
                    808:                                zend_error(E_WARNING, "%s::__toString() must return a string", ce_exception->name);
                    809:                        } else {
                    810:                                zend_update_property_string(default_exception_ce, exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name : Z_STRVAL_P(str) TSRMLS_CC);
                    811:                        }
                    812:                }
                    813:                zval_ptr_dtor(&str);
                    814: 
                    815:                if (EG(exception)) {
                    816:                        /* do the best we can to inform about the inner exception */
                    817:                        if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
                    818:                                file = zend_read_property(default_exception_ce, EG(exception), "file", sizeof("file")-1, 1 TSRMLS_CC);
                    819:                                line = zend_read_property(default_exception_ce, EG(exception), "line", sizeof("line")-1, 1 TSRMLS_CC);
1.1.1.3 ! misho     820: 
        !           821:                                convert_to_string(file);
        !           822:                                file = (Z_STRLEN_P(file) > 0) ? file : NULL;
        !           823:                                line = (Z_TYPE_P(line) == IS_LONG) ? line : NULL;
1.1       misho     824:                        } else {
                    825:                                file = NULL;
                    826:                                line = NULL;
                    827:                        }
                    828:                        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);
                    829:                }
                    830: 
                    831:                str = zend_read_property(default_exception_ce, exception, "string", sizeof("string")-1, 1 TSRMLS_CC);
                    832:                file = zend_read_property(default_exception_ce, exception, "file", sizeof("file")-1, 1 TSRMLS_CC);
                    833:                line = zend_read_property(default_exception_ce, exception, "line", sizeof("line")-1, 1 TSRMLS_CC);
                    834: 
1.1.1.3 ! misho     835:                convert_to_string(str);
        !           836:                convert_to_string(file);
        !           837:                convert_to_long(line);
        !           838: 
        !           839:                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));
1.1       misho     840:        } else {
                    841:                zend_error(severity, "Uncaught exception '%s'", ce_exception->name);
                    842:        }
                    843: }
                    844: /* }}} */
                    845: 
                    846: ZEND_API void zend_throw_exception_object(zval *exception TSRMLS_DC) /* {{{ */
                    847: {
                    848:        zend_class_entry *exception_ce;
                    849: 
                    850:        if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
                    851:                zend_error(E_ERROR, "Need to supply an object when throwing an exception");
                    852:        }
                    853: 
                    854:        exception_ce = Z_OBJCE_P(exception);
                    855: 
                    856:        if (!exception_ce || !instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
                    857:                zend_error(E_ERROR, "Exceptions must be valid objects derived from the Exception base class");
                    858:        }
                    859:        zend_throw_exception_internal(exception TSRMLS_CC);
                    860: }
                    861: /* }}} */
                    862: 
                    863: /*
                    864:  * Local variables:
                    865:  * tab-width: 4
                    866:  * c-basic-offset: 4
                    867:  * indent-tabs-mode: t
                    868:  * End:
                    869:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>