Back to index

php5  5.3.10
zend_exceptions.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: Andi Gutmans <andi@zend.com>                                |
00016    |          Marcus Boerger <helly@php.net>                              |
00017    |          Sterling Hughes <sterling@php.net>                          |
00018    |          Zeev Suraski <zeev@zend.com>                                |
00019    +----------------------------------------------------------------------+
00020 */
00021 
00022 /* $Id: zend_exceptions.c 321634 2012-01-01 13:15:04Z felipe $ */
00023 
00024 #include "zend.h"
00025 #include "zend_API.h"
00026 #include "zend_builtin_functions.h"
00027 #include "zend_interfaces.h"
00028 #include "zend_exceptions.h"
00029 #include "zend_vm.h"
00030 
00031 zend_class_entry *default_exception_ce;
00032 zend_class_entry *error_exception_ce;
00033 static zend_object_handlers default_exception_handlers;
00034 ZEND_API void (*zend_throw_exception_hook)(zval *ex TSRMLS_DC);
00035 
00036 void zend_exception_set_previous(zval *exception, zval *add_previous TSRMLS_DC)
00037 {
00038        zval *previous;
00039 
00040        if (exception == add_previous || !add_previous || !exception) {
00041               return;
00042        }
00043        if (Z_TYPE_P(add_previous) != IS_OBJECT && !instanceof_function(Z_OBJCE_P(add_previous), default_exception_ce TSRMLS_CC)) {
00044               zend_error(E_ERROR, "Cannot set non exception as previous exception");
00045               return;
00046        }
00047        while (exception && exception != add_previous && Z_OBJ_HANDLE_P(exception) != Z_OBJ_HANDLE_P(add_previous)) {
00048               previous = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 1 TSRMLS_CC);
00049               if (Z_TYPE_P(previous) == IS_NULL) {
00050                      zend_update_property(default_exception_ce, exception, "previous", sizeof("previous")-1, add_previous TSRMLS_CC);
00051                      Z_DELREF_P(add_previous);
00052                      return;
00053               }
00054               exception = previous;
00055        }
00056 }
00057 
00058 void zend_exception_save(TSRMLS_D) /* {{{ */
00059 {
00060        if (EG(prev_exception)) {
00061               zend_exception_set_previous(EG(exception), EG(prev_exception) TSRMLS_CC);
00062        }
00063        if (EG(exception)) {
00064               EG(prev_exception) = EG(exception);
00065        }
00066        EG(exception) = NULL;
00067 }
00068 /* }}} */
00069 
00070 void zend_exception_restore(TSRMLS_D) /* {{{ */
00071 {
00072        if (EG(prev_exception)) {
00073               if (EG(exception)) {
00074                      zend_exception_set_previous(EG(exception), EG(prev_exception) TSRMLS_CC);
00075               } else {
00076                      EG(exception) = EG(prev_exception);
00077               }
00078               EG(prev_exception) = NULL;
00079        }
00080 }
00081 /* }}} */
00082 
00083 void zend_throw_exception_internal(zval *exception TSRMLS_DC) /* {{{ */
00084 {
00085        if (exception != NULL) {
00086               zval *previous = EG(exception);
00087               zend_exception_set_previous(exception, EG(exception) TSRMLS_CC);
00088               EG(exception) = exception;
00089               if (previous) {
00090                      return;
00091               }
00092        }
00093        if (!EG(current_execute_data)) {
00094               if(EG(exception)) {
00095                      zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
00096               }
00097               zend_error(E_ERROR, "Exception thrown without a stack frame");
00098        }
00099 
00100        if (zend_throw_exception_hook) {
00101               zend_throw_exception_hook(exception TSRMLS_CC);
00102        }
00103 
00104        if (EG(current_execute_data)->opline == NULL ||
00105            (EG(current_execute_data)->opline+1)->opcode == ZEND_HANDLE_EXCEPTION) {
00106               /* no need to rethrow the exception */
00107               return;
00108        }
00109        EG(opline_before_exception) = EG(current_execute_data)->opline;
00110        EG(current_execute_data)->opline = EG(exception_op);
00111 }
00112 /* }}} */
00113 
00114 ZEND_API void zend_clear_exception(TSRMLS_D) /* {{{ */
00115 {
00116        if (EG(prev_exception)) {
00117               zval_ptr_dtor(&EG(prev_exception));
00118               EG(prev_exception) = NULL;
00119        }
00120        if (!EG(exception)) {
00121               return;
00122        }
00123        zval_ptr_dtor(&EG(exception));
00124        EG(exception) = NULL;
00125        EG(current_execute_data)->opline = EG(opline_before_exception);
00126 #if ZEND_DEBUG
00127        EG(opline_before_exception) = NULL;
00128 #endif
00129 }
00130 /* }}} */
00131 
00132 static zend_object_value zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces TSRMLS_DC) /* {{{ */
00133 {
00134        zval tmp, obj;
00135        zend_object *object;
00136        zval *trace;
00137 
00138        Z_OBJVAL(obj) = zend_objects_new(&object, class_type TSRMLS_CC);
00139        Z_OBJ_HT(obj) = &default_exception_handlers;
00140 
00141        ALLOC_HASHTABLE(object->properties);
00142        zend_hash_init(object->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
00143        zend_hash_copy(object->properties, &class_type->default_properties, zval_copy_property_ctor(class_type), (void *) &tmp, sizeof(zval *));
00144 
00145        ALLOC_ZVAL(trace);
00146        Z_UNSET_ISREF_P(trace);
00147        Z_SET_REFCOUNT_P(trace, 0);
00148        zend_fetch_debug_backtrace(trace, skip_top_traces, 0 TSRMLS_CC);
00149 
00150        zend_update_property_string(default_exception_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename(TSRMLS_C) TSRMLS_CC);
00151        zend_update_property_long(default_exception_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
00152        zend_update_property(default_exception_ce, &obj, "trace", sizeof("trace")-1, trace TSRMLS_CC);
00153 
00154        return Z_OBJVAL(obj);
00155 }
00156 /* }}} */
00157 
00158 static zend_object_value zend_default_exception_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
00159 {
00160        return zend_default_exception_new_ex(class_type, 0 TSRMLS_CC);
00161 }
00162 /* }}} */
00163 
00164 static zend_object_value zend_error_exception_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
00165 {
00166        return zend_default_exception_new_ex(class_type, 2 TSRMLS_CC);
00167 }
00168 /* }}} */
00169 
00170 /* {{{ proto Exception Exception::__clone()
00171    Clone the exception object */
00172 ZEND_METHOD(exception, __clone)
00173 {
00174        /* Should never be executable */
00175        zend_throw_exception(NULL, "Cannot clone object using __clone()", 0 TSRMLS_CC);
00176 }
00177 /* }}} */
00178 
00179 /* {{{ proto Exception::__construct(string message, int code [, Exception previous])
00180    Exception constructor */
00181 ZEND_METHOD(exception, __construct)
00182 {
00183        char  *message = NULL;
00184        long   code = 0;
00185        zval  *object, *previous = NULL;
00186        int    argc = ZEND_NUM_ARGS(), message_len;
00187 
00188        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC, "|slO!", &message, &message_len, &code, &previous, default_exception_ce) == FAILURE) {
00189               zend_error(E_ERROR, "Wrong parameters for Exception([string $exception [, long $code [, Exception $previous = NULL]]])");
00190        }
00191 
00192        object = getThis();
00193 
00194        if (message) {
00195               zend_update_property_string(default_exception_ce, object, "message", sizeof("message")-1, message TSRMLS_CC);
00196        }
00197 
00198        if (code) {
00199               zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
00200        }
00201 
00202        if (previous) {
00203               zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
00204        }
00205 }
00206 /* }}} */
00207 
00208 /* {{{ proto ErrorException::__construct(string message, int code, int severity [, string filename [, int lineno [, Exception previous]]])
00209    ErrorException constructor */
00210 ZEND_METHOD(error_exception, __construct)
00211 {
00212        char  *message = NULL, *filename = NULL;
00213        long   code = 0, severity = E_ERROR, lineno;
00214        zval  *object, *previous = NULL;
00215        int    argc = ZEND_NUM_ARGS(), message_len, filename_len;
00216 
00217        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) {
00218               zend_error(E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno  [, Exception $previous = NULL]]]]]])");
00219        }
00220 
00221        object = getThis();
00222 
00223        if (message) {
00224               zend_update_property_string(default_exception_ce, object, "message", sizeof("message")-1, message TSRMLS_CC);
00225        }
00226 
00227        if (code) {
00228               zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code TSRMLS_CC);
00229        }
00230 
00231        if (previous) {
00232               zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous TSRMLS_CC);
00233        }
00234 
00235        zend_update_property_long(default_exception_ce, object, "severity", sizeof("severity")-1, severity TSRMLS_CC);
00236 
00237        if (argc >= 4) {
00238            zend_update_property_string(default_exception_ce, object, "file", sizeof("file")-1, filename TSRMLS_CC);
00239        if (argc < 5) {
00240            lineno = 0; /* invalidate lineno */
00241        }
00242        zend_update_property_long(default_exception_ce, object, "line", sizeof("line")-1, lineno TSRMLS_CC);
00243        }
00244 }
00245 /* }}} */
00246 
00247 #define DEFAULT_0_PARAMS \
00248        if (zend_parse_parameters_none() == FAILURE) { \
00249               return; \
00250        }
00251 
00252 static void _default_exception_get_entry(zval *object, char *name, int name_len, zval *return_value TSRMLS_DC) /* {{{ */
00253 {
00254        zval *value;
00255 
00256        value = zend_read_property(default_exception_ce, object, name, name_len, 0 TSRMLS_CC);
00257 
00258        *return_value = *value;
00259        zval_copy_ctor(return_value);
00260        INIT_PZVAL(return_value);
00261 }
00262 /* }}} */
00263 
00264 /* {{{ proto string Exception::getFile()
00265    Get the file in which the exception occurred */
00266 ZEND_METHOD(exception, getFile)
00267 {
00268        DEFAULT_0_PARAMS;
00269 
00270        _default_exception_get_entry(getThis(), "file", sizeof("file")-1, return_value TSRMLS_CC);
00271 }
00272 /* }}} */
00273 
00274 /* {{{ proto int Exception::getLine()
00275    Get the line in which the exception occurred */
00276 ZEND_METHOD(exception, getLine)
00277 {
00278        DEFAULT_0_PARAMS;
00279 
00280        _default_exception_get_entry(getThis(), "line", sizeof("line")-1, return_value TSRMLS_CC);
00281 }
00282 /* }}} */
00283 
00284 /* {{{ proto string Exception::getMessage()
00285    Get the exception message */
00286 ZEND_METHOD(exception, getMessage)
00287 {
00288        DEFAULT_0_PARAMS;
00289 
00290        _default_exception_get_entry(getThis(), "message", sizeof("message")-1, return_value TSRMLS_CC);
00291 }
00292 /* }}} */
00293 
00294 /* {{{ proto int Exception::getCode()
00295    Get the exception code */
00296 ZEND_METHOD(exception, getCode)
00297 {
00298        DEFAULT_0_PARAMS;
00299 
00300        _default_exception_get_entry(getThis(), "code", sizeof("code")-1, return_value TSRMLS_CC);
00301 }
00302 /* }}} */
00303 
00304 /* {{{ proto array Exception::getTrace()
00305    Get the stack trace for the location in which the exception occurred */
00306 ZEND_METHOD(exception, getTrace)
00307 {
00308        DEFAULT_0_PARAMS;
00309 
00310        _default_exception_get_entry(getThis(), "trace", sizeof("trace")-1, return_value TSRMLS_CC);
00311 }
00312 /* }}} */
00313 
00314 /* {{{ proto int ErrorException::getSeverity()
00315    Get the exception severity */
00316 ZEND_METHOD(error_exception, getSeverity)
00317 {
00318        DEFAULT_0_PARAMS;
00319 
00320        _default_exception_get_entry(getThis(), "severity", sizeof("severity")-1, return_value TSRMLS_CC);
00321 }
00322 /* }}} */
00323 
00324 /* {{{ gettraceasstring() macros */
00325 #define TRACE_APPEND_CHR(chr)                                            \
00326        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
00327        (*str)[(*len)++] = chr
00328 
00329 #define TRACE_APPEND_STRL(val, vallen)                                   \
00330        {                                                                    \
00331               int l = vallen;                                                  \
00332               *str = (char*)erealloc(*str, *len + l + 1);                      \
00333               memcpy((*str) + *len, val, l);                                   \
00334               *len += l;                                                       \
00335        }
00336 
00337 #define TRACE_APPEND_STR(val)                                            \
00338        TRACE_APPEND_STRL(val, sizeof(val)-1)
00339 
00340 #define TRACE_APPEND_KEY(key)                                            \
00341        if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
00342            TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
00343        }
00344 
00345 /* }}} */
00346 
00347 static int _build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
00348 {
00349        char **str;
00350        int *len;
00351 
00352        str = va_arg(args, char**);
00353        len = va_arg(args, int*);
00354 
00355        /* the trivial way would be to do:
00356         * conver_to_string_ex(arg);
00357         * append it and kill the now tmp arg.
00358         * but that could cause some E_NOTICE and also damn long lines.
00359         */
00360 
00361        switch (Z_TYPE_PP(arg)) {
00362               case IS_NULL:
00363                      TRACE_APPEND_STR("NULL, ");
00364                      break;
00365               case IS_STRING: {
00366                      int l_added;
00367                      TRACE_APPEND_CHR('\'');
00368                      if (Z_STRLEN_PP(arg) > 15) {
00369                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
00370                             TRACE_APPEND_STR("...', ");
00371                             l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
00372                      } else {
00373                             l_added = Z_STRLEN_PP(arg);
00374                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
00375                             TRACE_APPEND_STR("', ");
00376                             l_added += 3 + 1;
00377                      }
00378                      while (--l_added) {
00379                             if ((*str)[*len - l_added] < 32) {
00380                                    (*str)[*len - l_added] = '?';
00381                             }
00382                      }
00383                      break;
00384               }
00385               case IS_BOOL:
00386                      if (Z_LVAL_PP(arg)) {
00387                             TRACE_APPEND_STR("true, ");
00388                      } else {
00389                             TRACE_APPEND_STR("false, ");
00390                      }
00391                      break;
00392               case IS_RESOURCE:
00393                      TRACE_APPEND_STR("Resource id #");
00394                      /* break; */
00395               case IS_LONG: {
00396                      long lval = Z_LVAL_PP(arg);
00397                      char s_tmp[MAX_LENGTH_OF_LONG + 1];
00398                      int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
00399                      TRACE_APPEND_STRL(s_tmp, l_tmp);
00400                      TRACE_APPEND_STR(", ");
00401                      break;
00402               }
00403               case IS_DOUBLE: {
00404                      double dval = Z_DVAL_PP(arg);
00405                      char *s_tmp;
00406                      int l_tmp;
00407 
00408                      s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
00409                      l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
00410                      TRACE_APPEND_STRL(s_tmp, l_tmp);
00411                      /* %G already handles removing trailing zeros from the fractional part, yay */
00412                      efree(s_tmp);
00413                      TRACE_APPEND_STR(", ");
00414                      break;
00415               }
00416               case IS_ARRAY:
00417                      TRACE_APPEND_STR("Array, ");
00418                      break;
00419               case IS_OBJECT: {
00420                      char *class_name;
00421                      zend_uint class_name_len;
00422                      int dup;
00423 
00424                      TRACE_APPEND_STR("Object(");
00425 
00426                      dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
00427 
00428                      TRACE_APPEND_STRL(class_name, class_name_len);
00429                      if(!dup) {
00430                             efree(class_name);
00431                      }
00432 
00433                      TRACE_APPEND_STR("), ");
00434                      break;
00435               }
00436               default:
00437                      break;
00438        }
00439        return ZEND_HASH_APPLY_KEEP;
00440 }
00441 /* }}} */
00442 
00443 static int _build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
00444 {
00445        char *s_tmp, **str;
00446        int *len, *num;
00447        long line;
00448        HashTable *ht = Z_ARRVAL_PP(frame);
00449        zval **file, **tmp;
00450 
00451        str = va_arg(args, char**);
00452        len = va_arg(args, int*);
00453        num = va_arg(args, int*);
00454 
00455        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
00456        sprintf(s_tmp, "#%d ", (*num)++);
00457        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
00458        efree(s_tmp);
00459        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
00460               if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
00461                      line = Z_LVAL_PP(tmp);
00462               } else {
00463                      line = 0;
00464               }
00465               s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
00466               sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
00467               TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
00468               efree(s_tmp);
00469        } else {
00470               TRACE_APPEND_STR("[internal function]: ");
00471        }
00472        TRACE_APPEND_KEY("class");
00473        TRACE_APPEND_KEY("type");
00474        TRACE_APPEND_KEY("function");
00475        TRACE_APPEND_CHR('(');
00476        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
00477               int last_len = *len;
00478               zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)_build_trace_args, 2, str, len);
00479               if (last_len != *len) {
00480                      *len -= 2; /* remove last ', ' */
00481               }
00482        }
00483        TRACE_APPEND_STR(")\n");
00484        return ZEND_HASH_APPLY_KEEP;
00485 }
00486 /* }}} */
00487 
00488 /* {{{ proto string Exception::getTraceAsString()
00489    Obtain the backtrace for the exception as a string (instead of an array) */
00490 ZEND_METHOD(exception, getTraceAsString)
00491 {
00492        zval *trace;
00493        char *res, **str, *s_tmp;
00494        int res_len = 0, *len = &res_len, num = 0;
00495 
00496        DEFAULT_0_PARAMS;
00497        
00498        res = estrdup("");
00499        str = &res;
00500 
00501        trace = zend_read_property(default_exception_ce, getThis(), "trace", sizeof("trace")-1, 1 TSRMLS_CC);
00502        zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)_build_trace_string, 3, str, len, &num);
00503 
00504        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
00505        sprintf(s_tmp, "#%d {main}", num);
00506        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
00507        efree(s_tmp);
00508 
00509        res[res_len] = '\0'; 
00510        RETURN_STRINGL(res, res_len, 0); 
00511 }
00512 /* }}} */
00513 
00514 /* {{{ proto string Exception::getPrevious()
00515    Return previous Exception or NULL. */
00516 ZEND_METHOD(exception, getPrevious)
00517 {
00518        zval *previous;
00519 
00520        DEFAULT_0_PARAMS;
00521 
00522        previous = zend_read_property(default_exception_ce, getThis(), "previous", sizeof("previous")-1, 1 TSRMLS_CC);
00523        RETURN_ZVAL(previous, 1, 0);
00524 }
00525 
00526 int zend_spprintf(char **message, int max_len, char *format, ...) /* {{{ */
00527 {
00528        va_list arg;
00529        int len;
00530 
00531        va_start(arg, format);
00532        len = zend_vspprintf(message, max_len, format, arg);
00533        va_end(arg);
00534        return len;
00535 }
00536 /* }}} */
00537 
00538 /* {{{ proto string Exception::__toString()
00539    Obtain the string representation of the Exception object */
00540 ZEND_METHOD(exception, __toString)
00541 {
00542        zval message, file, line, *trace, *exception;
00543        char *str, *prev_str;
00544        int len = 0;
00545        zend_fcall_info fci;
00546        zval fname;
00547        
00548        DEFAULT_0_PARAMS;
00549        
00550        str = estrndup("", 0);
00551 
00552        exception = getThis();
00553        ZVAL_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1, 1);
00554 
00555        while (exception && Z_TYPE_P(exception) == IS_OBJECT) {
00556               prev_str = str;
00557               _default_exception_get_entry(exception, "message", sizeof("message")-1, &message TSRMLS_CC);
00558               _default_exception_get_entry(exception, "file", sizeof("file")-1, &file TSRMLS_CC);
00559               _default_exception_get_entry(exception, "line", sizeof("line")-1, &line TSRMLS_CC);
00560 
00561               convert_to_string(&message);
00562               convert_to_string(&file);
00563               convert_to_long(&line);
00564 
00565               fci.size = sizeof(fci);
00566               fci.function_table = &Z_OBJCE_P(exception)->function_table;
00567               fci.function_name = &fname;
00568               fci.symbol_table = NULL;
00569               fci.object_ptr = exception;
00570               fci.retval_ptr_ptr = &trace;
00571               fci.param_count = 0;
00572               fci.params = NULL;
00573               fci.no_separation = 1;
00574 
00575               zend_call_function(&fci, NULL TSRMLS_CC);
00576 
00577               if (Z_TYPE_P(trace) != IS_STRING) {
00578                      zval_ptr_dtor(&trace);
00579                      trace = NULL;
00580               }
00581 
00582               if (Z_STRLEN(message) > 0) {
00583                      len = zend_spprintf(&str, 0, "exception '%s' with message '%s' in %s:%ld\nStack trace:\n%s%s%s",
00584                                                         Z_OBJCE_P(exception)->name, Z_STRVAL(message), Z_STRVAL(file), Z_LVAL(line),
00585                                                         (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
00586                                                         len ? "\n\nNext " : "", prev_str);
00587               } else {
00588                      len = zend_spprintf(&str, 0, "exception '%s' in %s:%ld\nStack trace:\n%s%s%s",
00589                                                         Z_OBJCE_P(exception)->name, Z_STRVAL(file), Z_LVAL(line),
00590                                                         (trace && Z_STRLEN_P(trace)) ? Z_STRVAL_P(trace) : "#0 {main}\n",
00591                                                         len ? "\n\nNext " : "", prev_str);
00592               }
00593               efree(prev_str);
00594               zval_dtor(&message);
00595               zval_dtor(&file);
00596               zval_dtor(&line);
00597 
00598               exception = zend_read_property(default_exception_ce, exception, "previous", sizeof("previous")-1, 0 TSRMLS_CC);
00599 
00600               if (trace) {
00601                      zval_ptr_dtor(&trace);
00602               }
00603        }
00604        zval_dtor(&fname);
00605 
00606        /* We store the result in the private property string so we can access
00607         * the result in uncaught exception handlers without memleaks. */
00608        zend_update_property_string(default_exception_ce, getThis(), "string", sizeof("string")-1, str TSRMLS_CC);
00609 
00610        RETURN_STRINGL(str, len, 0);
00611 }
00612 /* }}} */
00613 
00614 /* {{{ internal structs */
00615 /* All functions that may be used in uncaught exception handlers must be final
00616  * and must not throw exceptions. Otherwise we would need a facility to handle
00617  * such exceptions in that handler.
00618  * Also all getXY() methods are final because thy serve as read only access to
00619  * their corresponding properties, no more, no less. If after all you need to
00620  * override somthing then it is method __toString().
00621  * And never try to change the state of exceptions and never implement anything
00622  * that gives the user anything to accomplish this.
00623  */
00624 ZEND_BEGIN_ARG_INFO_EX(arginfo_exception___construct, 0, 0, 0)
00625        ZEND_ARG_INFO(0, message)
00626        ZEND_ARG_INFO(0, code)
00627        ZEND_ARG_INFO(0, previous)
00628 ZEND_END_ARG_INFO()
00629 
00630 const static zend_function_entry default_exception_functions[] = {
00631        ZEND_ME(exception, __clone, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
00632        ZEND_ME(exception, __construct, arginfo_exception___construct, ZEND_ACC_PUBLIC)
00633        ZEND_ME(exception, getMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00634        ZEND_ME(exception, getCode, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00635        ZEND_ME(exception, getFile, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00636        ZEND_ME(exception, getLine, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00637        ZEND_ME(exception, getTrace, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00638        ZEND_ME(exception, getPrevious, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00639        ZEND_ME(exception, getTraceAsString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00640        ZEND_ME(exception, __toString, NULL, 0)
00641        {NULL, NULL, NULL}
00642 };
00643 
00644 ZEND_BEGIN_ARG_INFO_EX(arginfo_error_exception___construct, 0, 0, 0)
00645        ZEND_ARG_INFO(0, message)
00646        ZEND_ARG_INFO(0, code)
00647        ZEND_ARG_INFO(0, severity)
00648        ZEND_ARG_INFO(0, filename)
00649        ZEND_ARG_INFO(0, lineno)
00650        ZEND_ARG_INFO(0, previous)
00651 ZEND_END_ARG_INFO()
00652 
00653 static const zend_function_entry error_exception_functions[] = {
00654        ZEND_ME(error_exception, __construct, arginfo_error_exception___construct, ZEND_ACC_PUBLIC)
00655        ZEND_ME(error_exception, getSeverity, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
00656        {NULL, NULL, NULL}
00657 };
00658 /* }}} */
00659 
00660 void zend_register_default_exception(TSRMLS_D) /* {{{ */
00661 {
00662        zend_class_entry ce;
00663 
00664        INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions);
00665        default_exception_ce = zend_register_internal_class(&ce TSRMLS_CC);
00666        default_exception_ce->create_object = zend_default_exception_new;
00667        memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
00668        default_exception_handlers.clone_obj = NULL;
00669 
00670        zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED TSRMLS_CC);
00671        zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE TSRMLS_CC);
00672        zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED TSRMLS_CC);
00673        zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
00674        zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED TSRMLS_CC);
00675        zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
00676        zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE TSRMLS_CC);
00677 
00678        INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions);
00679        error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce, NULL TSRMLS_CC);
00680        error_exception_ce->create_object = zend_error_exception_new;
00681        zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED TSRMLS_CC);
00682 }
00683 /* }}} */
00684 
00685 ZEND_API zend_class_entry *zend_exception_get_default(TSRMLS_D) /* {{{ */
00686 {
00687        return default_exception_ce;
00688 }
00689 /* }}} */
00690 
00691 ZEND_API zend_class_entry *zend_get_error_exception(TSRMLS_D) /* {{{ */
00692 {
00693        return error_exception_ce;
00694 }
00695 /* }}} */
00696 
00697 ZEND_API zval * zend_throw_exception(zend_class_entry *exception_ce, char *message, long code TSRMLS_DC) /* {{{ */
00698 {
00699        zval *ex;
00700 
00701        MAKE_STD_ZVAL(ex);
00702        if (exception_ce) {
00703               if (!instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
00704                      zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class");
00705                      exception_ce = default_exception_ce;
00706               }
00707        } else {
00708               exception_ce = default_exception_ce;
00709        }
00710        object_init_ex(ex, exception_ce);
00711 
00712 
00713        if (message) {
00714               zend_update_property_string(default_exception_ce, ex, "message", sizeof("message")-1, message TSRMLS_CC);
00715        }
00716        if (code) {
00717               zend_update_property_long(default_exception_ce, ex, "code", sizeof("code")-1, code TSRMLS_CC);
00718        }
00719 
00720        zend_throw_exception_internal(ex TSRMLS_CC);
00721        return ex;
00722 }
00723 /* }}} */
00724 
00725 ZEND_API zval * zend_throw_exception_ex(zend_class_entry *exception_ce, long code TSRMLS_DC, char *format, ...) /* {{{ */
00726 {
00727        va_list arg;
00728        char *message;
00729        zval *zexception;
00730 
00731        va_start(arg, format);
00732        zend_vspprintf(&message, 0, format, arg);
00733        va_end(arg);
00734        zexception = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
00735        efree(message);
00736        return zexception;
00737 }
00738 /* }}} */
00739 
00740 ZEND_API zval * zend_throw_error_exception(zend_class_entry *exception_ce, char *message, long code, int severity TSRMLS_DC) /* {{{ */
00741 {
00742        zval *ex = zend_throw_exception(exception_ce, message, code TSRMLS_CC);
00743        zend_update_property_long(default_exception_ce, ex, "severity", sizeof("severity")-1, severity TSRMLS_CC);
00744        return ex;
00745 }
00746 /* }}} */
00747 
00748 static void zend_error_va(int type, const char *file, uint lineno, const char *format, ...) /* {{{ */
00749 {
00750        va_list args;
00751 
00752        va_start(args, format);
00753        zend_error_cb(type, file, lineno, format, args);
00754        va_end(args);
00755 }
00756 /* }}} */
00757 
00758 /* This function doesn't return if it uses E_ERROR */
00759 ZEND_API void zend_exception_error(zval *exception, int severity TSRMLS_DC) /* {{{ */
00760 {
00761        zend_class_entry *ce_exception = Z_OBJCE_P(exception);
00762        if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
00763               zval *str, *file, *line;
00764 
00765               EG(exception) = NULL;
00766 
00767               zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", &str);
00768               if (!EG(exception)) {
00769                      if (Z_TYPE_P(str) != IS_STRING) {
00770                             zend_error(E_WARNING, "%s::__toString() must return a string", ce_exception->name);
00771                      } else {
00772                             zend_update_property_string(default_exception_ce, exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name : Z_STRVAL_P(str) TSRMLS_CC);
00773                      }
00774               }
00775               zval_ptr_dtor(&str);
00776 
00777               if (EG(exception)) {
00778                      /* do the best we can to inform about the inner exception */
00779                      if (instanceof_function(ce_exception, default_exception_ce TSRMLS_CC)) {
00780                             file = zend_read_property(default_exception_ce, EG(exception), "file", sizeof("file")-1, 1 TSRMLS_CC);
00781                             line = zend_read_property(default_exception_ce, EG(exception), "line", sizeof("line")-1, 1 TSRMLS_CC);
00782                      } else {
00783                             file = NULL;
00784                             line = NULL;
00785                      }
00786                      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);
00787               }
00788 
00789               str = zend_read_property(default_exception_ce, exception, "string", sizeof("string")-1, 1 TSRMLS_CC);
00790               file = zend_read_property(default_exception_ce, exception, "file", sizeof("file")-1, 1 TSRMLS_CC);
00791               line = zend_read_property(default_exception_ce, exception, "line", sizeof("line")-1, 1 TSRMLS_CC);
00792 
00793               zend_error_va(severity, Z_STRVAL_P(file), Z_LVAL_P(line), "Uncaught %s\n  thrown", Z_STRVAL_P(str));
00794        } else {
00795               zend_error(severity, "Uncaught exception '%s'", ce_exception->name);
00796        }
00797 }
00798 /* }}} */
00799 
00800 ZEND_API void zend_throw_exception_object(zval *exception TSRMLS_DC) /* {{{ */
00801 {
00802        zend_class_entry *exception_ce;
00803 
00804        if (exception == NULL || Z_TYPE_P(exception) != IS_OBJECT) {
00805               zend_error(E_ERROR, "Need to supply an object when throwing an exception");
00806        }
00807 
00808        exception_ce = Z_OBJCE_P(exception);
00809 
00810        if (!exception_ce || !instanceof_function(exception_ce, default_exception_ce TSRMLS_CC)) {
00811               zend_error(E_ERROR, "Exceptions must be valid objects derived from the Exception base class");
00812        }
00813        zend_throw_exception_internal(exception TSRMLS_CC);
00814 }
00815 /* }}} */
00816 
00817 /*
00818  * Local variables:
00819  * tab-width: 4
00820  * c-basic-offset: 4
00821  * indent-tabs-mode: t
00822  * End:
00823  */