1
0
mirror of https://github.com/deuill/go-php.git synced 2024-09-21 08:50:45 +00:00
go-php/value/value.go

233 lines
5.8 KiB
Go
Raw Normal View History

// Copyright 2015 Alexander Palaistras. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
// Package value implements value transformation between Go and PHP contexts.
2015-10-01 23:48:33 +00:00
package value
2015-09-22 20:54:58 +00:00
2015-10-01 23:48:33 +00:00
// #cgo CFLAGS: -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM
// #cgo CFLAGS: -I/usr/include/php/Zend
// #cgo LDFLAGS: -lphp5
//
2015-09-22 20:54:58 +00:00
// #include <stdlib.h>
2015-09-22 21:14:51 +00:00
// #include <stdbool.h>
// #include <main/php.h>
2015-09-22 20:54:58 +00:00
// #include "value.h"
import "C"
import (
"fmt"
2015-10-01 21:57:17 +00:00
"reflect"
2015-09-22 20:54:58 +00:00
"unsafe"
)
// Kind represents the specific kind of type represented in Value.
type Kind int
const (
Null Kind = iota
Long
Double
Bool
Array
Object
String
)
2015-10-01 21:57:17 +00:00
// Value represents a PHP value.
2015-09-22 20:54:58 +00:00
type Value struct {
value *C.struct__engine_value
}
// Kind returns the Value's concrete kind of type.
func (v *Value) Kind() Kind {
return (Kind)(C.value_kind(v.value))
}
// Interface returns the internal PHP value as it lies, with no conversion step.
// Attempting to call this method on an object value will return `nil`, as
// conversion from objects to structs requires a struct definition to extract to.
func (v *Value) Interface() interface{} {
switch v.Kind() {
case Long:
return v.Int()
case Double:
return v.Float()
case Bool:
return v.Bool()
case String:
return v.String()
}
return nil
}
// Int returns the internal PHP value as an integer, converting if necessary.
func (v *Value) Int() int64 {
return (int64)(C.value_get_long(v.value))
}
// Float returns the internal PHP value as a floating point number, converting
// if necessary.
func (v *Value) Float() float64 {
return (float64)(C.value_get_double(v.value))
}
// Bool returns the internal PHP value as a boolean, converting if necessary.
func (v *Value) Bool() bool {
return (bool)(C.value_get_bool(v.value))
}
// String returns the internal PHP value as a string, converting if necessary.
func (v *Value) String() string {
return C.GoString(C.value_get_string(v.value))
2015-09-22 20:54:58 +00:00
}
// Ptr returns a pointer to the internal PHP value, and is mostly used for
// passing to C functions.
2015-09-22 20:54:58 +00:00
func (v *Value) Ptr() unsafe.Pointer {
return unsafe.Pointer(v.value)
2015-09-22 20:54:58 +00:00
}
// Destroy removes all active references to the internal PHP value and frees
// any resources used.
2015-09-22 20:54:58 +00:00
func (v *Value) Destroy() {
if v.value != nil {
C.value_destroy(v.value)
v.value = nil
}
2015-09-22 20:54:58 +00:00
}
var errInvalidType = func(v interface{}) error {
return fmt.Errorf("Cannot create value of unknown type '%T'", v)
}
// New creates a PHP value representtion 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
//
2015-10-17 15:00:16 +00:00
// 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.
2015-10-01 23:48:33 +00:00
func New(val interface{}) (*Value, error) {
var ptr *C.struct__engine_value
var err error
2015-09-22 20:54:58 +00:00
2015-09-26 19:40:41 +00:00
// Determine value type and create PHP value from the concrete type.
2015-10-01 21:57:17 +00:00
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()))
2015-10-01 21:57:17 +00:00
// Bind floating point number to PHP double type.
case reflect.Float64:
ptr, err = C.value_create_double(C.double(v.Float()))
2015-10-01 21:57:17 +00:00
// Bind boolean to PHP bool type.
case reflect.Bool:
ptr, err = C.value_create_bool(C.bool(v.Bool()))
2015-10-01 21:57:17 +00:00
// Bind string to PHP string type.
case reflect.String:
str := C.CString(v.String())
2015-09-22 20:54:58 +00:00
ptr, err = C.value_create_string(str)
2015-10-17 15:00:16 +00:00
C.free(unsafe.Pointer(str))
2015-10-01 21:57:17 +00:00
// Bind slice to PHP indexed array type.
case reflect.Slice:
if ptr, err = C.value_create_array(C.uint(v.Len())); err != nil {
break
}
2015-10-01 21:57:17 +00:00
for i := 0; i < v.Len(); i++ {
2015-10-01 23:48:33 +00:00
vs, err := New(v.Index(i).Interface())
2015-10-01 21:57:17 +00:00
if err != nil {
2015-10-17 15:00:16 +00:00
C.value_destroy(ptr)
2015-10-01 21:57:17 +00:00
return nil, err
}
C.value_array_set_index(ptr, C.ulong(i), vs.value)
2015-10-01 21:57:17 +00:00
}
// Bind map (with integer or string keys) to PHP associative array type.
case reflect.Map:
kt := v.Type().Key().Kind()
2015-09-22 20:54:58 +00:00
if kt == reflect.Int || kt == reflect.String {
if ptr, err = C.value_create_array(C.uint(v.Len())); err != nil {
break
}
2015-10-01 21:57:17 +00:00
for _, key := range v.MapKeys() {
kv, err := New(v.MapIndex(key).Interface())
2015-10-01 21:57:17 +00:00
if err != nil {
2015-10-17 15:00:16 +00:00
C.value_destroy(ptr)
2015-10-01 21:57:17 +00:00
return nil, err
}
if kt == reflect.Int {
C.value_array_set_index(ptr, C.ulong(key.Int()), kv.value)
2015-10-01 21:57:17 +00:00
} else {
str := C.CString(key.String())
2015-10-01 21:57:17 +00:00
C.value_array_set_key(ptr, str, kv.value)
2015-10-01 21:57:17 +00:00
C.free(unsafe.Pointer(str))
}
}
} else {
return nil, errInvalidType(val)
2015-10-01 21:57:17 +00:00
}
2015-10-17 15:00:16 +00:00
// Bind struct to PHP object (stdClass) type.
case reflect.Struct:
vt := v.Type()
if ptr, err = C.value_create_object(); err != nil {
break
}
2015-10-17 15:00:16 +00:00
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.value)
2015-10-17 15:00:16 +00:00
C.free(unsafe.Pointer(str))
}
2015-10-01 21:57:17 +00:00
default:
return nil, errInvalidType(val)
}
if err != nil {
return nil, fmt.Errorf("Unable to create PHP value from Go value '%v'", val)
2015-09-22 20:54:58 +00:00
}
return &Value{value: ptr}, nil
}
// NewFromPtr creates a Value type from an existing PHP value pointer.
func NewFromPtr(val unsafe.Pointer) (*Value, error) {
if val == nil {
return nil, fmt.Errorf("Cannot create value from `nil` pointer")
}
v, err := C.value_new((*C.zval)(val))
if err != nil {
return nil, fmt.Errorf("Unable to create PHP value from pointer")
}
return &Value{value: v}, nil
}