diff --git a/context/context.c b/context/context.c index 27105d1..5a99f2c 100644 --- a/context/context.c +++ b/context/context.c @@ -43,7 +43,7 @@ engine_context *context_new(void *parent) { void context_exec(engine_context *context, char *filename) { #ifdef ZTS - void ***tsrm_ls = *context->ptsrm_ls; + void ***tsrm_ls = *(context->ptsrm_ls); #endif // Attempt to execute script file. @@ -62,6 +62,20 @@ void context_exec(engine_context *context, char *filename) { return NULL; } +void context_eval(engine_context *context, char *script) { + #ifdef ZTS + void ***tsrm_ls = *(context->ptsrm_ls); + #endif + + // Attempt to evaluate inline script. + zend_first_try { + zend_eval_string(script, NULL, "" TSRMLS_CC); + } zend_end_try(); + + errno = 0; + return NULL; +} + void context_bind(engine_context *context, char *name, void *zvalptr) { zval *value = (zval *) zvalptr; diff --git a/context/context.go b/context/context.go index 6013294..91c8990 100644 --- a/context/context.go +++ b/context/context.go @@ -56,10 +56,10 @@ func (c *Context) Bind(name string, val interface{}) error { // context, and returns an error, if any. Output produced by the script is // written to the context's pre-defined io.Writer instance. func (c *Context) Exec(filename string) error { - name := C.CString(filename) - defer C.free(unsafe.Pointer(name)) + f := C.CString(filename) + defer C.free(unsafe.Pointer(f)) - _, err := C.context_exec(c.context, name) + _, err := C.context_exec(c.context, f) if err != nil { return fmt.Errorf("Error executing script '%s' in context", filename) } @@ -67,6 +67,20 @@ func (c *Context) Exec(filename string) error { return nil } +// Eval executes the PHP script contained in script, and follows the same +// semantics as the Exec function. +func (c *Context) Eval(script string) error { + s := C.CString(script) + defer C.free(unsafe.Pointer(s)) + + _, err := C.context_eval(c.context, s) + if err != nil { + return fmt.Errorf("Error executing script '%s' in context", script) + } + + return nil +} + // Write copies data in the context's pre-defined io.Writer instance. It is // largely used internally, but is part of the context's public API due to // architectural contstraints. diff --git a/context/context.h b/context/context.h index 5009c8e..119fb50 100644 --- a/context/context.h +++ b/context/context.h @@ -15,6 +15,7 @@ typedef struct _engine_context { engine_context *context_new(void *parent); void context_exec(engine_context *context, char *filename); +void context_eval(engine_context *context, char *script); void context_bind(engine_context *context, char *name, void *zvalptr); void context_destroy(engine_context *context); diff --git a/php_test.go b/php_test.go index 67a9075..496837c 100644 --- a/php_test.go +++ b/php_test.go @@ -88,6 +88,37 @@ func TestContextExec(t *testing.T) { } } +var evalTests = []struct { + script string // Script to run + expected string // Expected output +}{ + {"echo 'Hello World';", "Hello World"}, + {"$i = 10; $d = 20; echo $i + $d;", "30"}, +} + +func TestContextEval(t *testing.T) { + var w MockWriter + + e, _ := New() + ctx, _ := NewContext(&w) + + defer e.Destroy() + defer ctx.Destroy() + + for _, tt := range evalTests { + if err := ctx.Eval(tt.script); err != nil { + t.Errorf("Context.Eval(%s): %s", tt.script, err) + } + + actual := w.String() + w.Reset() + + if actual != tt.expected { + t.Errorf("Context.Eval(%s): expected '%s', actual '%s'", tt.script, tt.expected, actual) + } + } +} + var bindTests = []struct { value interface{} // Value to bind expected string // Serialized form of value