zend_exceptions.c 31 KB

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