@@ -123,17 +123,209 @@ ZEND_METHOD(exception, gettrace)
123
123
_default_exception_get_entry (getThis (), "trace" , sizeof ("trace" )- 1 , return_value TSRMLS_CC );
124
124
}
125
125
126
+ /* {{{ ZEND_METHOD(exception, gettraceasstring) */
127
+ #define TRACE_APPEND_CHR (chr ) \
128
+ *str = (char*)erealloc(*str, *len + 1 + 1); \
129
+ (*str)[(*len)++] = chr
130
+
131
+ #define TRACE_APPEND_STRL (val , vallen ) \
132
+ { \
133
+ int l = vallen; \
134
+ *str = (char*)erealloc(*str, *len + l + 1); \
135
+ memcpy((*str) + *len, val, l); \
136
+ *len += l; \
137
+ }
138
+
139
+ #define TRACE_APPEND_STR (val ) \
140
+ TRACE_APPEND_STRL(val, sizeof(val)-1)
141
+
142
+ #define TRACE_APPEND_KEY (key ) \
143
+ if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
144
+ TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
145
+ }
146
+
147
+ static int _build_trace_args (zval * * arg , int num_args , va_list args , zend_hash_key * hash_key )
148
+ {
149
+ char * * str ;
150
+ int * len ;
151
+
152
+ str = va_arg (args , char * * );
153
+ len = va_arg (args , int * );
154
+
155
+ /* the trivial way would be to do:
156
+ * conver_to_string_ex(arg);
157
+ * append it and kill the now tmp arg.
158
+ * but that could cause some E_NOTICE and also damn long lines.
159
+ */
160
+
161
+ switch (Z_TYPE_PP (arg )) {
162
+ case IS_NULL :
163
+ TRACE_APPEND_STR ("NULL, " );
164
+ break ;
165
+ case IS_STRING :
166
+ TRACE_APPEND_CHR ('\'' );
167
+ if (Z_STRLEN_PP (arg ) > 15 ) {
168
+ TRACE_APPEND_STRL (Z_STRVAL_PP (arg ), 15 );
169
+ TRACE_APPEND_STR ("...', " );
170
+ } else {
171
+ TRACE_APPEND_STRL (Z_STRVAL_PP (arg ), Z_STRLEN_PP (arg ));
172
+ TRACE_APPEND_STR ("', " );
173
+ }
174
+ break ;
175
+ case IS_BOOL :
176
+ if (Z_LVAL_PP (arg )) {
177
+ TRACE_APPEND_STR ("true, " );
178
+ } else {
179
+ TRACE_APPEND_STR ("false, " );
180
+ }
181
+ break ;
182
+ case IS_RESOURCE :
183
+ TRACE_APPEND_STR ("Resource id #" );
184
+ /* break; */
185
+ case IS_LONG : {
186
+ long lval = Z_LVAL_PP (arg );
187
+ char s_tmp [MAX_LENGTH_OF_LONG + 1 ];
188
+ int l_tmp = zend_sprintf (s_tmp , "%ld" , lval ); /* SAFE */
189
+ TRACE_APPEND_STRL (s_tmp , l_tmp );
190
+ TRACE_APPEND_STR (", " );
191
+ break ;
192
+ }
193
+ case IS_DOUBLE : {
194
+ double dval = Z_DVAL_PP (arg );
195
+ char * s_tmp ;
196
+ int l_tmp ;
197
+ TSRMLS_FETCH ();
198
+
199
+ s_tmp = emalloc (MAX_LENGTH_OF_DOUBLE + EG (precision ) + 1 );
200
+ l_tmp = zend_sprintf (s_tmp , "%.*G" , (int ) EG (precision ), dval ); /* SAFE */
201
+ TRACE_APPEND_STRL (s_tmp , l_tmp );
202
+ /* %G already handles removing trailing zeros from the fractional part, yay */
203
+ efree (s_tmp );
204
+ TRACE_APPEND_STR (", " );
205
+ break ;
206
+ }
207
+ case IS_ARRAY :
208
+ TRACE_APPEND_STR ("Array, " );
209
+ break ;
210
+ case IS_OBJECT : {
211
+ TSRMLS_FETCH ();
212
+
213
+ TRACE_APPEND_STR ("Object(" );
214
+ TRACE_APPEND_STRL (Z_OBJCE_PP (arg )-> name , strlen (Z_OBJCE_PP (arg )-> name ));
215
+ TRACE_APPEND_STR ("), " );
216
+ break ;
217
+ }
218
+ default :
219
+ break ;
220
+ }
221
+ return ZEND_HASH_APPLY_KEEP ;
222
+ }
223
+
224
+ static int _build_trace_string (zval * * frame , int num_args , va_list args , zend_hash_key * hash_key )
225
+ {
226
+ char * s_tmp , * * str ;
227
+ int * len ;
228
+ long line ;
229
+ HashTable * ht = Z_ARRVAL_PP (frame );
230
+ zval * * file , * * tmp ;
231
+
232
+ str = va_arg (args , char * * );
233
+ len = va_arg (args , int * );
234
+
235
+ if (zend_hash_find (ht , "file" , sizeof ("file" ), (void * * )& file ) == SUCCESS ) {
236
+ if (zend_hash_find (ht , "line" , sizeof ("line" ), (void * * )& tmp ) == SUCCESS ) {
237
+ line = Z_LVAL_PP (tmp );
238
+ } else {
239
+ line = 0 ;
240
+ }
241
+ s_tmp = emalloc (Z_STRLEN_PP (file ) + MAX_LENGTH_OF_LONG + 2 + 1 );
242
+ sprintf (s_tmp , "%s(%ld): " , Z_STRVAL_PP (file ), line );
243
+ TRACE_APPEND_STRL (s_tmp , strlen (s_tmp ));
244
+ efree (s_tmp );
245
+ } else {
246
+ TRACE_APPEND_STR ("[internal function]: " );
247
+ }
248
+ TRACE_APPEND_KEY ("class" );
249
+ TRACE_APPEND_KEY ("type" );
250
+ TRACE_APPEND_KEY ("function" );
251
+ TRACE_APPEND_CHR ('(' );
252
+ if (zend_hash_find (ht , "args" , sizeof ("args" ), (void * * )& tmp ) == SUCCESS ) {
253
+ zend_hash_apply_with_arguments (Z_ARRVAL_PP (tmp ), (apply_func_args_t )_build_trace_args , 2 , str , len );
254
+ * len -= 2 ; /* remove last ', ' */
255
+ }
256
+ TRACE_APPEND_STR (")\n" );
257
+ return ZEND_HASH_APPLY_KEEP ;
258
+ }
259
+
260
+ ZEND_METHOD (exception , gettraceasstring )
261
+ {
262
+ zval * trace ;
263
+ char * str = estrdup ("" );
264
+ int len = 0 ;
265
+
266
+ trace = zend_read_property (Z_OBJCE_P (getThis ()), getThis (), "trace" , sizeof ("trace" )- 1 , 1 TSRMLS_CC );
267
+ zend_hash_apply_with_arguments (Z_ARRVAL_P (trace ), (apply_func_args_t )_build_trace_string , 2 , & str , & len );
268
+
269
+ str [len ] = '\0' ;
270
+ RETURN_STRINGL (str , len , 0 );
271
+ }
272
+ /* }}} */
273
+
274
+ ZEND_METHOD (exception , tostring )
275
+ {
276
+ zval * message , * file , * line , * trace ;
277
+ char * str ;
278
+ int len ;
279
+ zend_fcall_info fci ;
280
+ zval fname ;
281
+
282
+ message = zend_read_property (Z_OBJCE_P (getThis ()), getThis (), "message" , sizeof ("message" )- 1 , 1 TSRMLS_CC );
283
+ file = zend_read_property (Z_OBJCE_P (getThis ()), getThis (), "file" , sizeof ("file" )- 1 , 1 TSRMLS_CC );
284
+ line = zend_read_property (Z_OBJCE_P (getThis ()), getThis (), "line" , sizeof ("line" )- 1 , 1 TSRMLS_CC );
285
+
286
+ ZVAL_STRINGL (& fname , "gettraceasstring" , sizeof ("gettraceasstring" )- 1 , 0 );
287
+
288
+ fci .size = sizeof (fci );
289
+ fci .function_table = & Z_OBJCE_P (getThis ())-> function_table ;
290
+ fci .function_name = & fname ;
291
+ fci .symbol_table = NULL ;
292
+ fci .object_pp = & getThis ();
293
+ fci .retval_ptr_ptr = & trace ;
294
+ fci .param_count = 0 ;
295
+ fci .params = NULL ;
296
+ fci .no_separation = 1 ;
297
+
298
+ zend_call_function (& fci , NULL TSRMLS_CC );
299
+
300
+ len = 11 + strlen (Z_OBJCE_P (getThis ())-> name ) + 16 + Z_STRLEN_P (message ) + 5 + Z_STRLEN_P (file ) + 1 + MAX_LENGTH_OF_LONG + 14 + Z_STRLEN_P (trace ) + 1 ;
301
+ str = emalloc (len );
302
+ sprintf (str , "exception '%s' with message '%s' in %s:%ld\nStack trace:\n%s" ,
303
+ Z_OBJCE_P (getThis ())-> name , Z_STRVAL_P (message ), Z_STRVAL_P (file ), Z_LVAL_P (line ),
304
+ Z_STRVAL_P (trace ));
305
+ len = strlen (str );
306
+
307
+ /* We store the result in the private property string so we can access
308
+ * the result in uncaught exception handlers without memleaks. */
309
+ zend_update_property_string (default_exception_ptr , getThis (), "string" , sizeof ("string" )- 1 , str TSRMLS_CC );
310
+
311
+ zval_ptr_dtor (& trace );
312
+
313
+ RETURN_STRINGL (str , len , 0 );
314
+ }
315
+
126
316
/* All functions that may be used in uncaught exception handlers must be final
127
317
* and must not throw exceptions. Otherwise we would need a facility to handle
128
318
* such exceptions in that handler.
129
319
*/
130
320
static zend_function_entry default_exception_functions [] = {
131
321
ZEND_ME (exception , __construct , NULL , 0 )
132
- ZEND_ME (exception , getmessage , NULL , 0 ) /* non final for now */
322
+ ZEND_ME (exception , getmessage , NULL , ZEND_ACC_PUBLIC | ZEND_ACC_FINAL )
133
323
ZEND_ME (exception , getcode , NULL , ZEND_ACC_PUBLIC |ZEND_ACC_FINAL )
134
324
ZEND_ME (exception , getfile , NULL , ZEND_ACC_PUBLIC |ZEND_ACC_FINAL )
135
325
ZEND_ME (exception , getline , NULL , ZEND_ACC_PUBLIC |ZEND_ACC_FINAL )
136
326
ZEND_ME (exception , gettrace , NULL , ZEND_ACC_PUBLIC |ZEND_ACC_FINAL )
327
+ ZEND_ME (exception , gettraceasstring , NULL , ZEND_ACC_PUBLIC |ZEND_ACC_FINAL )
328
+ ZEND_ME (exception , tostring , NULL , 0 )
137
329
{NULL , NULL , NULL }
138
330
};
139
331
@@ -146,6 +338,7 @@ static void zend_register_default_exception(TSRMLS_D)
146
338
default_exception_ptr -> create_object = zend_default_exception_new ;
147
339
148
340
zend_declare_property_string (default_exception_ptr , "message" , sizeof ("message" )- 1 , "Unknown exception" , ZEND_ACC_PROTECTED );
341
+ zend_declare_property_string (default_exception_ptr , "string" , sizeof ("string" )- 1 , "" , ZEND_ACC_PRIVATE );
149
342
zend_declare_property_long (default_exception_ptr , "code" , sizeof ("code" )- 1 , 0 , ZEND_ACC_PROTECTED );
150
343
zend_declare_property_null (default_exception_ptr , "file" , sizeof ("file" )- 1 , ZEND_ACC_PROTECTED );
151
344
zend_declare_property_null (default_exception_ptr , "line" , sizeof ("line" )- 1 , ZEND_ACC_PROTECTED );
@@ -252,10 +445,10 @@ ZEND_API void zend_exception_error(zval *exception TSRMLS_DC)
252
445
if (instanceof_function (Z_OBJCE_P (exception ), default_exception_ptr TSRMLS_CC )) {
253
446
zval * message = zend_read_property (default_exception_ptr , exception , "message" , sizeof ("message" )- 1 , 1 TSRMLS_CC );
254
447
zval * file = zend_read_property (default_exception_ptr , exception , "file" , sizeof ("file" )- 1 , 1 TSRMLS_CC );
255
- zval * lineno = zend_read_property (default_exception_ptr , exception , "line" , sizeof ("line" )- 1 , 1 TSRMLS_CC );
256
- zend_error_va (E_ERROR , Z_STRVAL_P (file ), Z_LVAL_P (lineno ), "Uncaught exception '%s' with message '%s'" , Z_OBJCE_P (exception )-> name , Z_STRVAL_P (message ));
448
+ zval * line = zend_read_property (default_exception_ptr , exception , "line" , sizeof ("line" )- 1 , 1 TSRMLS_CC );
449
+ zend_error_va (E_ERROR , Z_STRVAL_P (file ), Z_LVAL_P (line ), "Uncaught exception '%s' with message '%s'" , Z_OBJCE_P (exception )-> name , Z_STRVAL_P (message ));
257
450
} else {
258
- zend_error (E_ERROR , "Uncaught exception '%s'! " , Z_OBJCE_P (exception )-> name );
451
+ zend_error (E_ERROR , "Uncaught exception '%s'" , Z_OBJCE_P (exception )-> name );
259
452
}
260
453
}
261
454
0 commit comments