diff --git a/php_test.go b/php_test.go index 4788a55..02977bf 100644 --- a/php_test.go +++ b/php_test.go @@ -120,12 +120,29 @@ var bindTests = []struct { value interface{} // Value to bind expected string // Serialized form of value }{ + // Integer to integer. {42, "i:42;"}, + // Float to double. {3.14159, "d:3.1415899999999999;"}, + // Boolean to boolean. {true, "b:1;"}, + // String to string. {"Such bind", `s:9:"Such bind";`}, + // Simple slice of strings to indexed array. {[]string{"this", "that"}, `a:2:{i:0;s:4:"this";i:1;s:4:"that";}`}, + // Nested slice of integers to indexed array. {[][]int{[]int{1, 2}, []int{3}}, `a:2:{i:0;a:2:{i:0;i:1;i:1;i:2;}i:1;a:1:{i:0;i:3;}}`}, + // Struct to object, with nested struct. + {struct { + I int + C string + F struct { + G bool + } + h bool + }{3, "test", struct { + G bool + }{false}, true}, `O:8:"stdClass":3:{s:1:"I";i:3;s:1:"C";s:4:"test";s:1:"F";O:8:"stdClass":1:{s:1:"G";b:0;}}`}, } func TestContextBind(t *testing.T) { diff --git a/value/value.c b/value/value.c index 45de593..2a5fb15 100644 --- a/value/value.c +++ b/value/value.c @@ -62,6 +62,19 @@ void value_array_set_key(void *arr, const char *key, void *val) { add_assoc_zval((zval *) arr, key, (zval *) val); } +void *value_create_object() { + zval *v; + + MAKE_STD_ZVAL(v); + object_init(v); + + return (void *) v; +} + +void value_object_add_property(void *obj, const char *key, void *val) { + add_property_zval((zval *) obj, key, (zval *) val); +} + void value_destroy(void *zvalptr) { zval_dtor((zval *) zvalptr); } \ No newline at end of file diff --git a/value/value.go b/value/value.go index 88d53d9..08705a3 100644 --- a/value/value.go +++ b/value/value.go @@ -53,9 +53,10 @@ func (v *Value) Destroy() { // map[int|string] -> associative array // struct -> object // -// Bindings for functions and method receivers to PHP functions and classes are -// only available in the engine scope, and must be predeclared before context -// execution. +// It is only possible to bind maps with integer or string keys. Only exported +// struct fields are passed to the PHP context. Bindings for functions and method +// receivers to PHP functions and classes are only available in the engine scope, +// and must be predeclared before context execution. func New(val interface{}) (*Value, error) { var ptr unsafe.Pointer @@ -74,9 +75,9 @@ func New(val interface{}) (*Value, error) { // Bind string to PHP string type. case reflect.String: str := C.CString(v.String()) - defer C.free(unsafe.Pointer(str)) ptr = C.value_create_string(str) + C.free(unsafe.Pointer(str)) // Bind slice to PHP indexed array type. case reflect.Slice: ptr = C.value_create_array(C.uint(v.Len())) @@ -84,6 +85,7 @@ func New(val interface{}) (*Value, error) { for i := 0; i < v.Len(); i++ { vs, err := New(v.Index(i).Interface()) if err != nil { + C.value_destroy(ptr) return nil, err } @@ -99,6 +101,7 @@ func New(val interface{}) (*Value, error) { for _, key := range v.MapKeys() { kv, err := New(v.MapIndex(key).Interface()) if err != nil { + C.value_destroy(ptr) return nil, err } @@ -114,6 +117,28 @@ func New(val interface{}) (*Value, error) { } else { return nil, errInvalidType } + // Bind struct to PHP object (stdClass) type. + case reflect.Struct: + vt := v.Type() + ptr = C.value_create_object() + + for i := 0; i < v.NumField(); i++ { + // Skip unexported fields. + if vt.Field(i).PkgPath != "" { + continue + } + + fv, err := New(v.Field(i).Interface()) + if err != nil { + C.value_destroy(ptr) + return nil, err + } + + str := C.CString(vt.Field(i).Name) + + C.value_object_add_property(ptr, str, fv.Ptr()) + C.free(unsafe.Pointer(str)) + } default: return nil, errInvalidType } diff --git a/value/value.h b/value/value.h index 59d8ecf..c8bfe48 100644 --- a/value/value.h +++ b/value/value.h @@ -12,6 +12,8 @@ void *value_create_string(char *value); void *value_create_array(unsigned int size); void value_array_set_index(void *arr, unsigned long idx, void *val); void value_array_set_key(void *arr, const char *key, void *val); +void *value_create_object(); +void value_object_add_property(void *obj, const char *key, void *val); void value_destroy(void *zvalptr); #endif \ No newline at end of file