From d542f85808d857dda8f94902e28b5c87718e4384 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 1 Oct 2018 23:49:08 +0200 Subject: [PATCH 1/3] Continue running the go program after exit() in PHP --- context.c | 18 ++++++++---- context.go | 24 +++++++++++++-- context_test.go | 65 +++++++++++++++++++++++++++++++++++++++++ include/context.h | 4 +-- include/php7/_context.h | 2 +- src/php5/_context.c | 7 ++--- src/php7/_context.c | 9 +++--- 7 files changed, 110 insertions(+), 19 deletions(-) diff --git a/context.c b/context.c index bc8ebc4..8e45d75 100644 --- a/context.c +++ b/context.c @@ -36,7 +36,7 @@ engine_context *context_new() { return context; } -void context_exec(engine_context *context, char *filename) { +void context_exec(engine_context *context, char *filename, int *exit) { int ret; // Attempt to execute script file. @@ -48,9 +48,11 @@ void context_exec(engine_context *context, char *filename) { script.opened_path = NULL; script.free_filename = 0; - ret = php_execute_script(&script); + ret = zend_execute_scripts(ZEND_REQUIRE, NULL, 1, &script); + *exit = -1; } zend_catch { - errno = 1; + errno = 0; + *exit = EG(exit_status); return; } zend_end_try(); @@ -63,7 +65,7 @@ void context_exec(engine_context *context, char *filename) { return; } -void *context_eval(engine_context *context, char *script) { +void *context_eval(engine_context *context, char *script, int *exit) { zval *str = _value_init(); _value_set_string(&str, script); @@ -84,7 +86,13 @@ void *context_eval(engine_context *context, char *script) { // Attempt to execute compiled string. zval tmp; - _context_eval(op, &tmp); + _context_eval(op, &tmp, exit); + + // Script called exit() + if (*exit != -1) { + errno = 0; + return NULL; + } // Allocate result value and copy temporary execution result in. zval *result = malloc(sizeof(zval)); diff --git a/context.go b/context.go index ca51427..a167679 100644 --- a/context.go +++ b/context.go @@ -34,6 +34,14 @@ type Context struct { values []*Value } +type ExitError struct { + Status int +} + +func (e *ExitError) Error() string { + return fmt.Sprintf("Exitcode %d", e.Status) +} + // Bind allows for binding Go values into the current execution context under // a certain name. Bind returns an error if attempting to bind an invalid value // (check the documentation for NewValue for what is considered to be a "valid" @@ -59,12 +67,18 @@ func (c *Context) Bind(name string, val interface{}) error { func (c *Context) Exec(filename string) error { f := C.CString(filename) defer C.free(unsafe.Pointer(f)) + var e C.int - _, err := C.context_exec(c.context, f) + _, err := C.context_exec(c.context, f, &e) if err != nil { return fmt.Errorf("Error executing script '%s' in context", filename) } + code := int(e) + if code != -1 { + return &ExitError{code} + } + return nil } @@ -74,12 +88,18 @@ func (c *Context) Exec(filename string) error { func (c *Context) Eval(script string) (*Value, error) { s := C.CString(script) defer C.free(unsafe.Pointer(s)) + var e C.int - result, err := C.context_eval(c.context, s) + result, err := C.context_eval(c.context, s, &e) if err != nil { return nil, fmt.Errorf("Error executing script '%s' in context", script) } + code := int(e) + if code != -1 { + return nil, &ExitError{code} + } + defer C.free(result) val, err := NewValueFromPtr(result) diff --git a/context_test.go b/context_test.go index a2948da..f2bf165 100644 --- a/context_test.go +++ b/context_test.go @@ -283,6 +283,71 @@ func TestContextBind(t *testing.T) { c.Destroy() } +var exitTests = []struct { + code int + script string +}{ + { + 0, + "exit(0);", + }, + { + 1, + "exit(1);", + }, + { + 255, + "exit(255);", + }, +} + +func TestContextExecExit(t *testing.T) { + c, _ := e.NewContext() + + for _, tt := range exitTests { + script, err := NewScript("exit", " Date: Tue, 2 Oct 2018 00:06:41 +0200 Subject: [PATCH 2/3] Set error_reporting to E_ALL to ensure TestContextLog test works --- engine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine.c b/engine.c index 4c87203..8930a07 100644 --- a/engine.c +++ b/engine.c @@ -19,6 +19,7 @@ const char engine_ini_defaults[] = { "expose_php = 0\n" "default_mimetype =\n" "html_errors = 0\n" + "error_reporting = E_ALL\n" "register_argc_argv = 1\n" "implicit_flush = 1\n" "output_buffering = 0\n" From 1896d38f9be41bc7c6a7ebd6bdad29c4b33bf25f Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Tue, 2 Oct 2018 13:21:57 +0200 Subject: [PATCH 3/3] Ensure triggering a fatal error will yield an ExitError Fixes #31 --- context_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/context_test.go b/context_test.go index f2bf165..5024216 100644 --- a/context_test.go +++ b/context_test.go @@ -284,8 +284,8 @@ func TestContextBind(t *testing.T) { } var exitTests = []struct { - code int - script string + code int + script string }{ { 0, @@ -299,13 +299,17 @@ var exitTests = []struct { 255, "exit(255);", }, + { + 255, + "trigger_error('test', E_USER_ERROR);", + }, } func TestContextExecExit(t *testing.T) { c, _ := e.NewContext() for _, tt := range exitTests { - script, err := NewScript("exit", "