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