Abstract
PHP interpreter is written in C, but uses try/catch construct known from other popular languages. This approach is different from everyday C-like error handling, where it's done by using function return value or by modifying error variable passed as an argument to a function.
zend_try { PG(during_request_startup) = 1; php_output_activate(TSRMLS_C); /* unimportant stuff */ } zend_catch { retval = FAILURE; } zend_end_try();
If one of instructions in a "try" block raises an exception then the evaluation of this block is terminated and execution flow moves to a "catch" block.
How it works?
The trick lie in use of setjmp, longjonp and sigsetjmp from setjmp.h. On some systems it's preferred to use theirs replacements: sigsetjmp, siglongjmp, sigjmp_buf. This nuance was hidden in below macros (Zend/zend.h):
#include <setjmp.h> #ifdef HAVE_SIGSETJMP # define SETJMP(a) sigsetjmp(a, 0) # define LONGJMP(a,b) siglongjmp(a, b) # define JMP_BUF sigjmp_buf #else # define SETJMP(a) setjmp(a) # define LONGJMP(a,b) longjmp(a, b) # define JMP_BUF jmp_buf #endif
It breaks Rule 20.7 of MISRA C coding standard (setjmp and longjmp shall not be used). In Zend/zend.h are defined macros mentioned in the first listing:
#define zend_try \ { \ JMP_BUF *__orig_bailout = EG(bailout); \ JMP_BUF __bailout; \ \ EG(bailout) = &__bailout; \ if (SETJMP(__bailout)==0) { #define zend_catch \ } else { \ EG(bailout) = __orig_bailout; #define zend_end_try() \ } \ EG(bailout) = __orig_bailout; \ }
It breaks Rule 19.4 of MISRA C coding standard (all brackets in a macro should be balanced). EG points us to Zend/zend.c that shows how to rise exceptions:
BEGIN_EXTERN_C() ZEND_API void _zend_bailout(char *filename, uint lineno) /* {{{ */ { TSRMLS_FETCH(); if (!EG(bailout)) { zend_output_debug_string(1, "%s(%d) : Bailed out without a bailout address!", filename, lineno); exit(-1); } CG(unclean_shutdown) = 1; CG(in_compilation) = EG(in_execution) = 0; EG(current_execute_data) = NULL; LONGJMP(*EG(bailout), FAILURE); }
I isolated this code and created my own application that uses it. It can be downloaded by cloning git@github.com:RobertGawron/snippets.git (ExceptionHandlingInC directory), as in original version, it can also handle nested try/catch constructions.
What's your opinion about this idea? I think that it shouldn't be used because the code is less readable, in addition I don't like macros and I try to avoid them if possible.
php never stops to amaze me (in negative sense ofc)
ReplyDeleteI agree, IMO above trick is pointless and obfuscate the code.
Delete