diff --git a/README.md b/README.md index 92254b4..4ca8600 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,37 @@ func main() { The above will execute script file `index.php` located in the current folder and will write any output to the `io.Writer` assigned to `Context.Output` (in this case, the standard output). +### Binding and returning variables + +The following example demonstrates binding a Go variable to the running PHP context, and returning a PHP variable for use in Go: + +```go +package main + +import ( + "fmt" + php "github.com/deuill/go-php" +) + +func main() { + engine, _ := php.New() + context, _ := engine.NewContext() + + var str string = "Hello" + context.Bind("var", str) + + val, _ := context.Eval("return $var.' World';") + fmt.Printf("%s", val.Interface()) + // Prints 'Hello World' back to the user. + + engine.Destroy() +} +``` + +A string value "Hello" is attached using `Context.Bind` under a name `var` (available in PHP as `$var`). A script is executed inline using `Context.Eval`, combinding the attached value with a PHP string and returning it to the user. + +Finally, the value is returned as an `interface{}` using `Value.Interface()` (one could also use `Value.String()`, though the both are equivalent in this case). + ## License All code in this repository is covered by the terms of the MIT License, the full text of which can be found in the LICENSE file. diff --git a/engine/context.c b/engine/context.c index 115bb80..c5b73bf 100644 --- a/engine/context.c +++ b/engine/context.c @@ -65,22 +65,28 @@ void context_exec(engine_context *context, char *filename) { } void *context_eval(engine_context *context, char *script) { - int status; - zval tmp; + zval str; + VALUE_SET_STRING(&str, script); - // Attempt to evaluate inline script. - zend_first_try { - status = zend_eval_string(script, &tmp, "gophp-engine"); - } zend_catch { - errno = 1; - return NULL; - } zend_end_try(); + // Compile script value. + uint32_t compiler_options = CG(compiler_options); + CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL; + zend_op_array *op = zend_compile_string(&str, "gophp-engine"); + CG(compiler_options) = compiler_options; + + zval_dtor(&str); - if (status == FAILURE) { + // Return error if script failed to compile. + if (!op) { errno = 1; return NULL; } + // Attempt to execute compiled string. + zval tmp; + CONTEXT_EXECUTE(op, &tmp); + + // Allocate result value and copy temporary execution result in. zval *result = malloc(sizeof(zval)); value_copy(result, &tmp); diff --git a/engine/context.go b/engine/context.go index f3ad9d8..577716a 100644 --- a/engine/context.go +++ b/engine/context.go @@ -31,7 +31,7 @@ type Context struct { Header http.Header context *C.struct__engine_context - values map[string]*Value + values []*Value } // NewContext creates a new execution context for the active engine and returns @@ -39,7 +39,7 @@ type Context struct { func NewContext() (*Context, error) { ctx := &Context{ Header: make(http.Header), - values: make(map[string]*Value), + values: make([]*Value, 0), } ptr, err := C.context_new(unsafe.Pointer(ctx)) @@ -66,7 +66,7 @@ func (c *Context) Bind(name string, val interface{}) error { defer C.free(unsafe.Pointer(n)) C.context_bind(c.context, n, v.Ptr()) - c.values[name] = v + c.values = append(c.values, v) return nil } @@ -90,11 +90,7 @@ func (c *Context) Exec(filename string) error { // containing the PHP value returned by the expression, if any. Any output // produced is written context's pre-defined io.Writer instance. func (c *Context) Eval(script string) (*Value, error) { - // When PHP compiles code with a non-NULL return value expected, it simply - // prepends a `return` call to the code, thus breaking simple scripts that - // would otherwise work. Thus, we need to wrap the code in a closure, and - // call it immediately. - s := C.CString("call_user_func(function(){" + script + "});") + s := C.CString(script) defer C.free(unsafe.Pointer(s)) result, err := C.context_eval(c.context, s) @@ -109,6 +105,8 @@ func (c *Context) Eval(script string) (*Value, error) { return nil, err } + c.values = append(c.values, val) + return val, nil } diff --git a/engine/context_test.go b/engine/context_test.go index e81562c..e3faae1 100644 --- a/engine/context_test.go +++ b/engine/context_test.go @@ -260,19 +260,14 @@ func TestContextBind(t *testing.T) { c, _ := NewContext() c.Output = &w - script, err := NewScript("bind.php", "obj, t); \ - object_properties_init(&r->obj, t); \ - r->obj.handlers = &receiver_handlers; \ - return &r->obj; \ +#define RECEIVER_OBJECT_CREATE(r, t) do { \ + r = emalloc(sizeof(engine_receiver)); \ + memset(r, 0, sizeof(engine_receiver)); \ + zend_object_std_init(&r->obj, t); \ + object_properties_init(&r->obj, t); \ + r->obj.handlers = &receiver_handlers; \ + return &r->obj; \ } while (0) #define RECEIVER_OBJECT_DESTROY(r) do { \