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

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | Zend Engine                                                          |
                      4:    +----------------------------------------------------------------------+
1.1.1.5 ! misho       5:    | Copyright (c) 1998-2014 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()) {
1.1.1.4   misho      88:                const char *classname;
                     89:                zend_uint name_len;
1.1.1.2   misho      90: 
                     91:                if (exception != NULL) {
                     92:                        zend_get_object_classname(exception, &classname, &name_len TSRMLS_CC);
1.1.1.4   misho      93:                        DTRACE_EXCEPTION_THROWN((char *)classname);
1.1.1.2   misho      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: 
1.1.1.5 ! misho     363: 
        !           364: #define TRACE_ARG_APPEND(vallen)                                                               \
        !           365:        *str = (char*)erealloc(*str, *len + 1 + vallen);                                        \
        !           366:        memmove((*str) + *len - l_added + 1 + vallen, (*str) + *len - l_added + 1, l_added);
        !           367: 
1.1       misho     368: /* }}} */
                    369: 
                    370: static int _build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                    371: {
                    372:        char **str;
                    373:        int *len;
                    374: 
                    375:        str = va_arg(args, char**);
                    376:        len = va_arg(args, int*);
                    377: 
                    378:        /* the trivial way would be to do:
1.1.1.5 ! misho     379:         * convert_to_string_ex(arg);
1.1       misho     380:         * append it and kill the now tmp arg.
                    381:         * but that could cause some E_NOTICE and also damn long lines.
                    382:         */
                    383: 
                    384:        switch (Z_TYPE_PP(arg)) {
                    385:                case IS_NULL:
                    386:                        TRACE_APPEND_STR("NULL, ");
                    387:                        break;
                    388:                case IS_STRING: {
                    389:                        int l_added;
                    390:                        TRACE_APPEND_CHR('\'');
                    391:                        if (Z_STRLEN_PP(arg) > 15) {
                    392:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                    393:                                TRACE_APPEND_STR("...', ");
                    394:                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                    395:                        } else {
                    396:                                l_added = Z_STRLEN_PP(arg);
                    397:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                    398:                                TRACE_APPEND_STR("', ");
                    399:                                l_added += 3 + 1;
                    400:                        }
                    401:                        while (--l_added) {
1.1.1.5 ! misho     402:                                unsigned char chr = (*str)[*len - l_added];
        !           403:                                if (chr < 32 || chr == '\\' || chr > 126) {
        !           404:                                        (*str)[*len - l_added] = '\\';
        !           405: 
        !           406:                                        switch (chr) {
        !           407:                                                case '\n':
        !           408:                                                        TRACE_ARG_APPEND(1);
        !           409:                                                        (*str)[++(*len) - l_added] = 'n';
        !           410:                                                        break;
        !           411:                                                case '\r':
        !           412:                                                        TRACE_ARG_APPEND(1);
        !           413:                                                        (*str)[++(*len) - l_added] = 'r';
        !           414:                                                        break;
        !           415:                                                case '\t':
        !           416:                                                        TRACE_ARG_APPEND(1);
        !           417:                                                        (*str)[++(*len) - l_added] = 't';
        !           418:                                                        break;
        !           419:                                                case '\f':
        !           420:                                                        TRACE_ARG_APPEND(1);
        !           421:                                                        (*str)[++(*len) - l_added] = 'f';
        !           422:                                                        break;
        !           423:                                                case '\v':
        !           424:                                                        TRACE_ARG_APPEND(1);
        !           425:                                                        (*str)[++(*len) - l_added] = 'v';
        !           426:                                                        break;
        !           427: #ifndef PHP_WIN32
        !           428:                                                case '\e':
        !           429: #else
        !           430:                                                case VK_ESCAPE:
        !           431: #endif
        !           432:                                                        TRACE_ARG_APPEND(1);
        !           433:                                                        (*str)[++(*len) - l_added] = 'e';
        !           434:                                                        break;
        !           435:                                                case '\\':
        !           436:                                                        TRACE_ARG_APPEND(1);
        !           437:                                                        (*str)[++(*len) - l_added] = '\\';
        !           438:                                                        break;
        !           439:                                                default:
        !           440:                                                        TRACE_ARG_APPEND(3);
        !           441:                                                        (*str)[*len - l_added + 1] = 'x';
        !           442:                                                        if ((chr >> 4) < 10) {
        !           443:                                                                (*str)[*len - l_added + 2] = (chr >> 4) + '0';
        !           444:                                                        } else {
        !           445:                                                                (*str)[*len - l_added + 2] = (chr >> 4) + 'A' - 10;
        !           446:                                                        }
        !           447:                                                        if (chr % 16 < 10) {
        !           448:                                                                (*str)[*len - l_added + 3] = chr % 16 + '0';
        !           449:                                                        } else {
        !           450:                                                                (*str)[*len - l_added + 3] = chr % 16 + 'A' - 10;
        !           451:                                                        }
        !           452:                                                        *len += 3;
        !           453:                                        }
1.1       misho     454:                                }
                    455:                        }
                    456:                        break;
                    457:                }
                    458:                case IS_BOOL:
                    459:                        if (Z_LVAL_PP(arg)) {
                    460:                                TRACE_APPEND_STR("true, ");
                    461:                        } else {
                    462:                                TRACE_APPEND_STR("false, ");
                    463:                        }
                    464:                        break;
                    465:                case IS_RESOURCE:
                    466:                        TRACE_APPEND_STR("Resource id #");
                    467:                        /* break; */
                    468:                case IS_LONG: {
                    469:                        long lval = Z_LVAL_PP(arg);
                    470:                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                    471:                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                    472:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                    473:                        TRACE_APPEND_STR(", ");
                    474:                        break;
                    475:                }
                    476:                case IS_DOUBLE: {
                    477:                        double dval = Z_DVAL_PP(arg);
                    478:                        char *s_tmp;
                    479:                        int l_tmp;
                    480: 
                    481:                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                    482:                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                    483:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                    484:                        /* %G already handles removing trailing zeros from the fractional part, yay */
                    485:                        efree(s_tmp);
                    486:                        TRACE_APPEND_STR(", ");
                    487:                        break;
                    488:                }
                    489:                case IS_ARRAY:
                    490:                        TRACE_APPEND_STR("Array, ");
                    491:                        break;
                    492:                case IS_OBJECT: {
1.1.1.2   misho     493:                        const char *class_name;
1.1       misho     494:                        zend_uint class_name_len;
                    495:                        int dup;
                    496: 
                    497:                        TRACE_APPEND_STR("Object(");
                    498: 
                    499:                        dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
                    500: 
                    501:                        TRACE_APPEND_STRL(class_name, class_name_len);
                    502:                        if(!dup) {
1.1.1.2   misho     503:                                efree((char*)class_name);
1.1       misho     504:                        }
                    505: 
                    506:                        TRACE_APPEND_STR("), ");
                    507:                        break;
                    508:                }
                    509:                default:
                    510:                        break;
                    511:        }
                    512:        return ZEND_HASH_APPLY_KEEP;
                    513: }
                    514: /* }}} */
                    515: 
                    516: static int _build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                    517: {
                    518:        char *s_tmp, **str;
                    519:        int *len, *num;
                    520:        long line;
                    521:        HashTable *ht = Z_ARRVAL_PP(frame);
                    522:        zval **file, **tmp;
                    523: 
1.1.1.3   misho     524:        if (Z_TYPE_PP(frame) != IS_ARRAY) {
                    525:                zend_error(E_WARNING, "Expected array for frame %lu", hash_key->h);
                    526:                return ZEND_HASH_APPLY_KEEP;
                    527:        }
                    528: 
1.1       misho     529:        str = va_arg(args, char**);
                    530:        len = va_arg(args, int*);
                    531:        num = va_arg(args, int*);
                    532: 
                    533:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
                    534:        sprintf(s_tmp, "#%d ", (*num)++);
                    535:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                    536:        efree(s_tmp);
                    537:        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1.1.1.3   misho     538:                if (Z_TYPE_PP(file) != IS_STRING) {
                    539:                        zend_error(E_WARNING, "Function name is no string");
                    540:                        TRACE_APPEND_STR("[unknown function]");
                    541:                } else{
                    542:                        if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
                    543:                                if (Z_TYPE_PP(tmp) == IS_LONG) {
                    544:                                        line = Z_LVAL_PP(tmp);
                    545:                                } else {
                    546:                                        zend_error(E_WARNING, "Line is no long");
                    547:                                        line = 0;
                    548:                                }
                    549:                        } else {
                    550:                                line = 0;
                    551:                        }
                    552:                        s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
                    553:                        sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
                    554:                        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                    555:                        efree(s_tmp);
1.1       misho     556:                }
                    557:        } else {
                    558:                TRACE_APPEND_STR("[internal function]: ");
                    559:        }
                    560:        TRACE_APPEND_KEY("class");
                    561:        TRACE_APPEND_KEY("type");
                    562:        TRACE_APPEND_KEY("function");
                    563:        TRACE_APPEND_CHR('(');
                    564:        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1.1.1.3   misho     565:                if (Z_TYPE_PP(tmp) == IS_ARRAY) {
                    566:                        int last_len = *len;
                    567:                        zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)_build_trace_args, 2, str, len);
                    568:                        if (last_len != *len) {
                    569:                                *len -= 2; /* remove last ', ' */
                    570:                        }
                    571:                } else {
                    572:                        zend_error(E_WARNING, "args element is no array");
1.1       misho     573:                }
                    574:        }
                    575:        TRACE_APPEND_STR(")\n");
                    576:        return ZEND_HASH_APPLY_KEEP;
                    577: }
                    578: /* }}} */
                    579: 
                    580: /* {{{ proto string Exception::getTraceAsString()
                    581:    Obtain the backtrace for the exception as a string (instead of an array) */
                    582: ZEND_METHOD(exception, getTraceAsString)
                    583: {
                    584:        zval *trace;
                    585:        char *res, **str, *s_tmp;
                    586:        int res_len = 0, *len = &res_len, num = 0;
                    587: 
                    588:        DEFAULT_0_PARAMS;
                    589:        
                    590:        res = estrdup("");
                    591:        str = &res;
                    592: 
                    593:        trace = zend_read_property(default_exception_ce, getThis(), "trace", sizeof("trace")-1, 1 TSRMLS_CC);
                    594:        zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)_build_trace_string, 3, str, len, &num);
                    595: 
                    596:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
                    597:        sprintf(s_tmp, "#%d {main}", num);
                    598:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                    599:        efree(s_tmp);
                    600: 
                    601:        res[res_len] = '\0';    
                    602:        RETURN_STRINGL(res, res_len, 0); 
                    603: }
                    604: /* }}} */
                    605: 
                    606: /* {{{ proto string Exception::getPrevious()
                    607:    Return previous Exception or NULL. */
                    608: ZEND_METHOD(exception, getPrevious)
                    609: {
                    610:        zval *previous;
                    611: 
                    612:        DEFAULT_0_PARAMS;
                    613: 
                    614:        previous = zend_read_property(default_exception_ce, getThis(), "previous", sizeof("previous")-1, 1 TSRMLS_CC);
                    615:        RETURN_ZVAL(previous, 1, 0);
                    616: }
                    617: 
                    618: int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
                    619: {
                    620:        va_list arg;
                    621:        int len;
                    622: 
                    623:        va_start(arg, format);
                    624:        len = zend_vspprintf(message, max_len, format, arg);
                    625:        va_end(arg);
                    626:        return len;
                    627: }
                    628: /* }}} */
                    629: 
                    630: /* {{{ proto string Exception::__toString()
                    631:    Obtain the string representation of the Exception object */
                    632: ZEND_METHOD(exception, __toString)
                    633: {
                    634:        zval message, file, line, *trace, *exception;
                    635:        char *str, *prev_str;
                    636:        int len = 0;
                    637:        zend_fcall_info fci;
                    638:        zval fname;
                    639:        
                    640:        DEFAULT_0_PARAMS;
                    641:        
                    642:        str = estrndup("", 0);
                    643: 
                    644:        exception = getThis();
                    645:        ZVAL_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
                    646: 
                    647:        while (exception && Z_TYPE_P(exception) == IS_OBJECT) {
                    648:                prev_str = str;
                    649:                _default_exception_get_entry(exception, "message", sizeof("message")-1, &message TSRMLS_CC);
                    650:                _default_exception_get_entry(exception, "file", sizeof("file")-1, &file TSRMLS_CC);
                    651:                _default_exception_get_entry(exception, "line", sizeof("line")-1, &line TSRMLS_CC);
                    652: 
                    653:                convert_to_string(&message);
                    654:                convert_to_string(&file);
                    655:                convert_to_long(&line);
                    656: 
                    657:                fci.size = sizeof(fci);
                    658:                fci.function_table = &Z_OBJCE_P(exception)->function_table;
                    659:                fci.function_name = &fname;
                    660:                fci.symbol_table = NULL;
                    661:                fci.object_ptr = exception;
                    662:                fci.retval_ptr_ptr = &trace;
                    663:                fci.param_count = 0;
                    664:                fci.params = NULL;
                    665:                fci.no_separation = 1;
                    666: 
                    667:                zend_call_function(&fci, NULL TSRMLS_CC);
                    668: 
                    669:                if (Z_TYPE_P(trace) != IS_STRING) {
                    670:                        zval_ptr_dtor(&trace);
                    671:                        trace = NULL;
                    672:                }
                    673: 
                    674:                if (Z_STRLEN(message) > 0) {
                    675:                        len = zend_spprintf(&str, 0, "exception '%s' with message '%s' in %s:%ld\nStack trace:\n%s%s%s",
                    676:                                                                Z_OBJCE_P(exception)->name, Z_STRVAL(message), Z_STRVAL(file), Z_LVAL(line),
                    677:                                                                (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
                    678:                                                                len ? "\n\nNext " : "", prev_str);
                    679:                } else {
                    680:                        len = zend_spprintf(&str, 0, "exception '%s' in %s:%ld\nStack trace:\n%s%s%s",
                    681:                                                                Z_OBJCE_P(exception)->name, Z_STRVAL(file), Z_LVAL(line),
                    682:                                                                (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
                    683:                                                                len ? "\n\nNext " : "", prev_str);
                    684:                }
                    685:                efree(prev_str);
                    686:                zval_dtor(&message);
                    687:                zval_dtor(&file);
                    688:                zval_dtor(&line);
                    689: 
                    690:                exception = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 0 TSRMLS_CC);
                    691: 
                    692:                if (trace) {
                    693:                        zval_ptr_dtor(&trace);
                    694:                }
1.1.1.2   misho     695: 
1.1       misho     696:        }
                    697:        zval_dtor(&fname);
                    698: 
                    699:        /* We store the result in the private property string so we can access
                    700:         * the result in uncaught exception handlers without memleaks. */
                    701:        zend_update_property_string(default_exception_ce, getThis(), "string", sizeof("string")-1, str TSRMLS_CC);
                    702: 
                    703:        RETURN_STRINGL(str, len, 0);
                    704: }
                    705: /* }}} */
                    706: 
                    707: /* {{{ internal structs */
                    708: /* All functions that may be used in uncaught exception handlers must be final
                    709:  * and must not throw exceptions. Otherwise we would need a facility to handle
                    710:  * such exceptions in that handler.
                    711:  * Also all getXY() methods are final because thy serve as read only access to
                    712:  * their corresponding properties, no more, no less. If after all you need to
                    713:  * override somthing then it is method __toString().
                    714:  * And never try to change the state of exceptions and never implement anything
                    715:  * that gives the user anything to accomplish this.
                    716:  */
                    717: ZEND_BEGIN_ARG_INFO_EX(arginfo_exception___construct, 0, 0, 0)
                    718:        ZEND_ARG_INFO(0, message)
                    719:        ZEND_ARG_INFO(0, code)
                    720:        ZEND_ARG_INFO(0, previous)
                    721: ZEND_END_ARG_INFO()
                    722: 
                    723: const static zend_function_entry default_exception_functions[] = {
                    724:        ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
                    725:        ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
                    726:        ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    727:        ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    728:        ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    729:        ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    730:        ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    731:        ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    732:        ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    733:        ZEND_ME(exception, __toString, NULL, 0)
                    734:        {NULL, NULL, NULL}
                    735: };
                    736: 
                    737: ZEND_BEGIN_ARG_INFO_EX(arginfo_error_exception___construct, 0, 0, 0)
                    738:        ZEND_ARG_INFO(0, message)
                    739:        ZEND_ARG_INFO(0, code)
                    740:        ZEND_ARG_INFO(0, severity)
                    741:        ZEND_ARG_INFO(0, filename)
                    742:        ZEND_ARG_INFO(0, lineno)
                    743:        ZEND_ARG_INFO(0, previous)
                    744: ZEND_END_ARG_INFO()
                    745: 
                    746: static const zend_function_entry error_exception_functions[] = {
                    747:        ZEND_ME(error_exception, __construct, arginfo_error_exception___construct, ZEND_ACC_PUBLIC)
                    748:        ZEND_ME(error_exception, getSeverity, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
                    749:        {NULL, NULL, NULL}
                    750: };
                    751: /* }}} */
                    752: 
                    753: void zend_register_default_exception(TSRMLS_D) /* {{{ */
                    754: {
                    755:        zend_class_entry ce;
                    756: 
                    757:        INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
                    758:        default_exception_ce = zend_register_internal_class(&ce TSRMLS_CC);
                    759:        default_exception_ce->create_object = zend_default_exception_new;
                    760:        memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
                    761:        default_exception_handlers.clone_obj = NULL;
                    762: 
                    763:        zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC);
                    764:        zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC);
                    765:        zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED TSRMLS_CC);
                    766:        zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
                    767:        zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
                    768:        zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
                    769:        zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
                    770: 
                    771:        INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
                    772:        error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce, NULL TSRMLS_CC);
                    773:        error_exception_ce->create_object = zend_error_exception_new;
                    774:        zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED TSRMLS_CC);
                    775: }
                    776: /* }}} */
                    777: 
                    778: ZEND_API zend_class_entry *zend_exception_get_default(TSRMLS_D) /* {{{ */
                    779: {
                    780:        return default_exception_ce;
                    781: }
                    782: /* }}} */
                    783: 
                    784: ZEND_API zend_class_entry *zend_get_error_exception(TSRMLS_D) /* {{{ */
                    785: {
                    786:        return error_exception_ce;
                    787: }
                    788: /* }}} */
                    789: 
                    790: ZEND_API zval * zend_throw_exception(zend_class_entry *exception_ce, char *message, long code TSRMLS_DC) /* {{{ */
                    791: {
                    792:        zval *ex;
                    793: 
                    794:        MAKE_STD_ZVAL(ex);
                    795:        if (exception_ce) {
                    796:                if (!instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
                    797:                        zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class");
                    798:                        exception_ce = default_exception_ce;
                    799:                }
                    800:        } else {
                    801:                exception_ce = default_exception_ce;
                    802:        }
                    803:        object_init_ex(ex, exception_ce);
                    804: 
                    805: 
                    806:        if (message) {
                    807:                zend_update_property_string(default_exception_ce, ex, "message", sizeof("message")-1, message TSRMLS_CC);
                    808:        }
                    809:        if (code) {
                    810:                zend_update_property_long(default_exception_ce, ex, "code", sizeof("code")-1, code TSRMLS_CC);
                    811:        }
                    812: 
                    813:        zend_throw_exception_internal(ex TSRMLS_CC);
                    814:        return ex;
                    815: }
                    816: /* }}} */
                    817: 
                    818: ZEND_API zval * zend_throw_exception_ex(zend_class_entry *exception_ce, long code TSRMLS_DC, char *format, ...) /* {{{ */
                    819: {
                    820:        va_list arg;
                    821:        char *message;
                    822:        zval *zexception;
                    823: 
                    824:        va_start(arg, format);
                    825:        zend_vspprintf(&message, 0, format, arg);
                    826:        va_end(arg);
                    827:        zexception = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
                    828:        efree(message);
                    829:        return zexception;
                    830: }
                    831: /* }}} */
                    832: 
                    833: ZEND_API zval * zend_throw_error_exception(zend_class_entry *exception_ce, char *message, long code, int severity TSRMLS_DC) /* {{{ */
                    834: {
                    835:        zval *ex = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
                    836:        zend_update_property_long(default_exception_ce, ex, "severity", sizeof("severity")-1, severity TSRMLS_CC);
                    837:        return ex;
                    838: }
                    839: /* }}} */
                    840: 
                    841: static void zend_error_va(int type, const char *file, uint lineno, const char *format, ...) /* {{{ */
                    842: {
                    843:        va_list args;
                    844: 
                    845:        va_start(args, format);
                    846:        zend_error_cb(type, file, lineno, format, args);
                    847:        va_end(args);
                    848: }
                    849: /* }}} */
                    850: 
                    851: /* This function doesn't return if it uses E_ERROR */
                    852: ZEND_API void zend_exception_error(zval *exception, int severity TSRMLS_DC) /* {{{ */
                    853: {
                    854:        zend_class_entry *ce_exception = Z_OBJCE_P(exception);
                    855:        if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
                    856:                zval *str, *file, *line;
                    857: 
                    858:                EG(exception) = NULL;
                    859: 
                    860:                zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", &str);
                    861:                if (!EG(exception)) {
                    862:                        if (Z_TYPE_P(str) != IS_STRING) {
                    863:                                zend_error(E_WARNING, "%s::__toString() must return a string", ce_exception->name);
                    864:                        } else {
                    865:                                zend_update_property_string(default_exception_ce, exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name : Z_STRVAL_P(str) TSRMLS_CC);
                    866:                        }
                    867:                }
                    868:                zval_ptr_dtor(&str);
                    869: 
                    870:                if (EG(exception)) {
                    871:                        /* do the best we can to inform about the inner exception */
                    872:                        if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
                    873:                                file = zend_read_property(default_exception_ce, EG(exception), "file", sizeof("file")-1, 1 TSRMLS_CC);
                    874:                                line = zend_read_property(default_exception_ce, EG(exception), "line", sizeof("line")-1, 1 TSRMLS_CC);
1.1.1.3   misho     875: 
                    876:                                convert_to_string(file);
                    877:                                file = (Z_STRLEN_P(file) > 0) ? file : NULL;
                    878:                                line = (Z_TYPE_P(line) == IS_LONG) ? line : NULL;
1.1       misho     879:                        } else {
                    880:                                file = NULL;
                    881:                                line = NULL;
                    882:                        }
                    883:                        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);
                    884:                }
                    885: 
                    886:                str = zend_read_property(default_exception_ce, exception, "string", sizeof("string")-1, 1 TSRMLS_CC);
                    887:                file = zend_read_property(default_exception_ce, exception, "file", sizeof("file")-1, 1 TSRMLS_CC);
                    888:                line = zend_read_property(default_exception_ce, exception, "line", sizeof("line")-1, 1 TSRMLS_CC);
                    889: 
1.1.1.3   misho     890:                convert_to_string(str);
                    891:                convert_to_string(file);
                    892:                convert_to_long(line);
                    893: 
                    894:                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     895:        } else {
                    896:                zend_error(severity, "Uncaught exception '%s'", ce_exception->name);
                    897:        }
                    898: }
                    899: /* }}} */
                    900: 
                    901: ZEND_API void zend_throw_exception_object(zval *exception TSRMLS_DC) /* {{{ */
                    902: {
                    903:        zend_class_entry *exception_ce;
                    904: 
                    905:        if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
                    906:                zend_error(E_ERROR, "Need to supply an object when throwing an exception");
                    907:        }
                    908: 
                    909:        exception_ce = Z_OBJCE_P(exception);
                    910: 
                    911:        if (!exception_ce || !instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
                    912:                zend_error(E_ERROR, "Exceptions must be valid objects derived from the Exception base class");
                    913:        }
                    914:        zend_throw_exception_internal(exception TSRMLS_CC);
                    915: }
                    916: /* }}} */
                    917: 
                    918: /*
                    919:  * Local variables:
                    920:  * tab-width: 4
                    921:  * c-basic-offset: 4
                    922:  * indent-tabs-mode: t
                    923:  * End:
                    924:  */

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