From 6fa93f5160d15e60d69555bab6f37fa34d96c1b3 Mon Sep 17 00:00:00 2001 From: Alex Palaistras Date: Sun, 20 Sep 2015 01:16:43 +0100 Subject: [PATCH] First version of variable bindings to context. This commit contains an initial version of variable bindings to a context, along with tests. Currently supported types are integers, floating point numbers, and strings. --- context.c | 11 +++++++++++ context.go | 41 +++++++++++++++++++++++++++++++++++++++++ context.h | 1 + context_test.go | 41 +++++++++++++++++++++++++++++++++++++++-- engine.go | 2 +- engine_test.go | 6 ++++++ test/bind.php | 9 +++++++++ value.c | 41 +++++++++++++++++++++++++++++++++++++++++ value.h | 9 +++++++++ 9 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 test/bind.php create mode 100644 value.c create mode 100644 value.h diff --git a/context.c b/context.c index 1153e32..a265150 100644 --- a/context.c +++ b/context.c @@ -61,6 +61,17 @@ void context_exec(engine_context *context, char *filename) { return_multi(NULL, 0); } +void context_bind(engine_context *context, char *name, void *zvalptr) { + zval *value = (zval *) zvalptr; + + #ifdef ZTS + void ***tsrm_ls = context->engine->tsrm_ls; + #endif + + ZEND_SET_SYMBOL(EG(active_symbol_table), name, value); + return_multi(NULL, 0); +} + int context_write(engine_context *context, const char *message, unsigned int length) { int written = contextWrite(context->parent, (void *) message, length); if (written != length) { diff --git a/context.go b/context.go index cb38177..577de6e 100644 --- a/context.go +++ b/context.go @@ -4,6 +4,7 @@ package php // #include // #include "engine.h" // #include "context.h" +// #include "value.h" import "C" import ( @@ -15,6 +16,42 @@ import ( type Context struct { context *C.struct__engine_context writer io.Writer + zvals map[string]unsafe.Pointer +} + +func (c *Context) Bind(name string, value interface{}) error { + var zval unsafe.Pointer + var err error + + switch v := value.(type) { + case int: + zval, err = C.value_long(C.long(v)) + case float64: + zval, err = C.value_double(C.double(v)) + case string: + str := C.CString(v) + defer C.free(unsafe.Pointer(str)) + + zval, err = C.value_string(str) + default: + return fmt.Errorf("Cannot bind unknown type '%T'", v) + } + + if err != nil { + return fmt.Errorf("Binding value '%v' to context failed", value) + } + + n := C.CString(name) + defer C.free(unsafe.Pointer(n)) + + if _, err = C.context_bind(c.context, n, zval); err != nil { + C.value_destroy(zval) + return fmt.Errorf("Binding value '%v' to context failed", value) + } + + c.zvals[name] = zval + + return nil } func (c *Context) Exec(filename string) error { @@ -30,6 +67,10 @@ func (c *Context) Exec(filename string) error { } func (c *Context) Destroy() { + for _, zval := range c.zvals { + C.value_destroy(zval) + } + C.context_destroy(c.context) c = nil } diff --git a/context.h b/context.h index e81a2c9..e68d43c 100644 --- a/context.h +++ b/context.h @@ -8,6 +8,7 @@ typedef struct _engine_context { engine_context *context_new(php_engine *engine, void *parent); void context_exec(engine_context *context, char *filename); +void context_bind(engine_context *context, char *name, void *vptr); int context_write(engine_context *context, const char *message, unsigned int length); void context_destroy(engine_context *context); diff --git a/context_test.go b/context_test.go index c0be3cb..45b8411 100644 --- a/context_test.go +++ b/context_test.go @@ -3,6 +3,7 @@ package php import ( "os" "path" + "strconv" "testing" ) @@ -27,12 +28,48 @@ func TestContextExec(t *testing.T) { for _, tt := range execTests { file := path.Join(testDir, tt.file) if err := ctx.Exec(file); err != nil { - t.Errorf("ContextExec(%s): %s", tt.file, err) + t.Errorf("Context.Exec(%s): %s", tt.file, err) } actual := w.String() + w.Reset() + if actual != tt.expected { - t.Errorf("ContextExec(%s): expected '%s', actual '%s'", tt.file, tt.expected, actual) + t.Errorf("Context.Exec(%s): expected '%s', actual '%s'", tt.file, tt.expected, actual) + } + } +} + +var bindTests = []struct { + value interface{} // Value to bind + expected string // Serialized form of value +}{ + {42, "i:42;"}, // Integer + {3.14159, "d:3.1415899999999999;"}, // Floating point + {"Such bind", `s:9:"Such bind";`}, // String +} + +func TestContextBind(t *testing.T) { + var w MockWriter + + e, _ := New() + ctx, _ := e.NewContext(&w) + + defer e.Destroy() + defer ctx.Destroy() + + for i, tt := range bindTests { + if err := ctx.Bind(strconv.FormatInt(int64(i), 10), tt.value); err != nil { + t.Errorf("Context.Bind(%v): %s", tt.value, err) + } + + ctx.Exec(path.Join(testDir, "bind.php")) + + actual := w.String() + w.Reset() + + if actual != tt.expected { + t.Errorf("Context.Bind(%v): expected '%s', actual '%s'", tt.value, tt.expected, actual) } } } diff --git a/engine.go b/engine.go index f8b7c5e..8dddf17 100644 --- a/engine.go +++ b/engine.go @@ -20,7 +20,7 @@ type Engine struct { } func (e *Engine) NewContext(w io.Writer) (*Context, error) { - ctx := &Context{writer: w} + ctx := &Context{writer: w, zvals: make(map[string]unsafe.Pointer)} ptr, err := C.context_new(e.engine, unsafe.Pointer(ctx)) if err != nil { diff --git a/engine_test.go b/engine_test.go index bed4c8c..f7f3055 100644 --- a/engine_test.go +++ b/engine_test.go @@ -26,6 +26,12 @@ func (m *MockWriter) String() string { return string(m.buffer) } +func (m *MockWriter) Reset() { + if m.buffer != nil { + m.buffer = m.buffer[:0] + } +} + func TestNewEngineContext(t *testing.T) { e, err := New() if err != nil { diff --git a/test/bind.php b/test/bind.php new file mode 100644 index 0000000..b468808 --- /dev/null +++ b/test/bind.php @@ -0,0 +1,9 @@ + +#include + +// PHP includes. +#include
+ +// Local includes. +#include "engine.h" +#include "value.h" + +void *value_long(long int value) { + zval *v; + + MAKE_STD_ZVAL(v); + ZVAL_LONG(v, value); + + return_multi((void *) v, 0); +} + +void *value_double(double value) { + zval *v; + + MAKE_STD_ZVAL(v); + ZVAL_DOUBLE(v, value); + + return_multi((void *) v, 0); +} + +void *value_string(char *value) { + zval *v; + + MAKE_STD_ZVAL(v); + ZVAL_STRING(v, value, 1); + + return_multi((void *) v, 0); +} + +void value_destroy(void *zvalptr) { + zval_dtor((zval *) zvalptr); +} \ No newline at end of file diff --git a/value.h b/value.h new file mode 100644 index 0000000..3b0d6ca --- /dev/null +++ b/value.h @@ -0,0 +1,9 @@ +#ifndef VALUE_H +#define VALUE_H + +void *value_long(long int value); +void *value_double(double value); +void *value_string(char *value); +void value_destroy(void *zvalptr); + +#endif \ No newline at end of file