// New creates a PHP value representation of a Go value val. Available bindings // for Go to PHP types are: // // int -> integer // float64 -> double // bool -> boolean // string -> string // slice -> indexed array // map[int|string] -> associative array // struct -> object // // 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 *C.struct__engine_value var err error // Determine value type and create PHP value from the concrete type. v := reflect.ValueOf(val) switch v.Kind() { // Bind integer to PHP int type. case reflect.Int: ptr, err = C.value_create_long(C.long(v.Int())) // Bind floating point number to PHP double type. case reflect.Float64: ptr, err = C.value_create_double(C.double(v.Float())) // Bind boolean to PHP bool type. case reflect.Bool: ptr, err = C.value_create_bool(C.bool(v.Bool())) // Bind string to PHP string type. case reflect.String: str := C.CString(v.String()) ptr, err = C.value_create_string(str) C.free(unsafe.Pointer(str)) // Bind slice to PHP indexed array type. case reflect.Slice: if ptr, err = C.value_create_array(C.uint(v.Len())); err != nil { break } for i := 0; i < v.Len(); i++ { vs, err := New(v.Index(i).Interface()) if err != nil { C.value_destroy(ptr) return nil, err } C.value_array_next_set(ptr, vs.value) } // Bind map (with integer or string keys) to PHP associative array type. case reflect.Map: kt := v.Type().Key().Kind() if kt == reflect.Int || kt == reflect.String { if ptr, err = C.value_create_array(C.uint(v.Len())); err != nil { break } for _, key := range v.MapKeys() { kv, err := New(v.MapIndex(key).Interface()) if err != nil { C.value_destroy(ptr) return nil, err } if kt == reflect.Int { C.value_array_index_set(ptr, C.ulong(key.Int()), kv.value) } else { str := C.CString(key.String()) C.value_array_key_set(ptr, str, kv.value) C.free(unsafe.Pointer(str)) } } } else { return nil, errInvalidType(val) } // Bind struct to PHP object (stdClass) type. case reflect.Struct: vt := v.Type() if ptr, err = C.value_create_object(); err != nil { break } 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_property_add(ptr, str, fv.value) C.free(unsafe.Pointer(str)) } default: return nil, errInvalidType(val) } if err != nil { return nil, fmt.Errorf("Unable to create PHP value from Go value '%v'", val) } return &Value{value: ptr}, nil }
// NewValue creates a PHP value representation of a Go value val. Available // bindings for Go to PHP types are: // // int -> integer // float64 -> double // bool -> boolean // string -> string // slice -> indexed array // map[int|string] -> associative array // struct -> object // // 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 NewValue(val interface{}) (*Value, error) { ptr, err := C.value_new() if err != nil { return nil, fmt.Errorf("Unable to instantiate PHP value") } v := reflect.ValueOf(val) // Determine interface value type and create PHP value from the concrete type. switch v.Kind() { // Bind integer to PHP int type. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: C.value_set_long(ptr, C.long(v.Int())) // Bind floating point number to PHP double type. case reflect.Float32, reflect.Float64: C.value_set_double(ptr, C.double(v.Float())) // Bind boolean to PHP bool type. case reflect.Bool: C.value_set_bool(ptr, C.bool(v.Bool())) // Bind string to PHP string type. case reflect.String: str := C.CString(v.String()) defer C.free(unsafe.Pointer(str)) C.value_set_string(ptr, str) // Bind slice to PHP indexed array type. case reflect.Slice: C.value_set_array(ptr, C.uint(v.Len())) for i := 0; i < v.Len(); i++ { vs, err := NewValue(v.Index(i).Interface()) if err != nil { C.value_destroy(ptr) return nil, err } C.value_array_next_set(ptr, vs.value) } // Bind map (with integer or string keys) to PHP associative array type. case reflect.Map: kt := v.Type().Key().Kind() if kt == reflect.Int || kt == reflect.String { C.value_set_array(ptr, C.uint(v.Len())) for _, key := range v.MapKeys() { kv, err := NewValue(v.MapIndex(key).Interface()) if err != nil { C.value_destroy(ptr) return nil, err } if kt == reflect.Int { C.value_array_index_set(ptr, C.ulong(key.Int()), kv.value) } else { str := C.CString(key.String()) defer C.free(unsafe.Pointer(str)) C.value_array_key_set(ptr, str, kv.value) } } } else { return nil, fmt.Errorf("Unable to create value of unknown type '%T'", val) } // Bind struct to PHP object (stdClass) type. case reflect.Struct: C.value_set_object(ptr) vt := v.Type() for i := 0; i < v.NumField(); i++ { // Skip unexported fields. if vt.Field(i).PkgPath != "" { continue } fv, err := NewValue(v.Field(i).Interface()) if err != nil { C.value_destroy(ptr) return nil, err } str := C.CString(vt.Field(i).Name) defer C.free(unsafe.Pointer(str)) C.value_object_property_set(ptr, str, fv.value) } case reflect.Invalid: C.value_set_null(ptr) default: C.value_destroy(ptr) return nil, fmt.Errorf("Unable to create value of unknown type '%T'", val) } return &Value{value: ptr}, nil }