You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

264 lines
6.0 KiB

package php_serialize
import (
"bytes"
"fmt"
"strconv"
)
func Serialize(v PhpValue) (string, error) {
encoder := NewSerializer()
encoder.SetSerializedEncodeFunc(SerializedEncodeFunc(Serialize))
return encoder.Encode(v)
}
type Serializer struct {
lastErr error
encodeFunc SerializedEncodeFunc
}
func NewSerializer() *Serializer {
return &Serializer{}
}
func (self *Serializer) SetSerializedEncodeFunc(f SerializedEncodeFunc) {
self.encodeFunc = f
}
func (self *Serializer) Encode(v PhpValue) (string, error) {
var value bytes.Buffer
switch t := v.(type) {
default:
self.saveError(fmt.Errorf("php_serialize: Unknown type %T with value %#v", t, v))
case nil:
value = self.encodeNull()
case bool:
value = self.encodeBool(v)
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
value = self.encodeNumber(v)
case string:
value = self.encodeString(v, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true)
case PhpArray, map[PhpValue]PhpValue, PhpSlice:
value = self.encodeArray(v, true)
case *PhpObject:
value = self.encodeObject(v)
case *PhpObjectSerialized:
value = self.encodeSerialized(v)
case *PhpSplArray:
value = self.encodeSplArray(v)
}
return value.String(), self.lastErr
}
func (self *Serializer) encodeNull() (buffer bytes.Buffer) {
buffer.WriteRune(TOKEN_NULL)
buffer.WriteRune(SEPARATOR_VALUES)
return
}
func (self *Serializer) encodeBool(v PhpValue) (buffer bytes.Buffer) {
buffer.WriteRune(TOKEN_BOOL)
buffer.WriteRune(SEPARATOR_VALUE_TYPE)
if bVal, ok := v.(bool); ok && bVal {
buffer.WriteString("1")
} else {
buffer.WriteString("0")
}
buffer.WriteRune(SEPARATOR_VALUES)
return
}
func (self *Serializer) encodeNumber(v PhpValue) (buffer bytes.Buffer) {
var val string
isFloat := false
switch intVal := v.(type) {
default:
val = "0"
case int:
val = strconv.FormatInt(int64(intVal), 10)
case int8:
val = strconv.FormatInt(int64(intVal), 10)
case int16:
val = strconv.FormatInt(int64(intVal), 10)
case int32:
val = strconv.FormatInt(int64(intVal), 10)
case int64:
val = strconv.FormatInt(int64(intVal), 10)
case uint:
val = strconv.FormatUint(uint64(intVal), 10)
case uint8:
val = strconv.FormatUint(uint64(intVal), 10)
case uint16:
val = strconv.FormatUint(uint64(intVal), 10)
case uint32:
val = strconv.FormatUint(uint64(intVal), 10)
case uint64:
val = strconv.FormatUint(uint64(intVal), 10)
// PHP has precision = 17 by default
case float32:
val = strconv.FormatFloat(float64(intVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 32)
isFloat = true
case float64:
val = strconv.FormatFloat(float64(intVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 64)
isFloat = true
}
if isFloat {
buffer.WriteRune(TOKEN_FLOAT)
} else {
buffer.WriteRune(TOKEN_INT)
}
buffer.WriteRune(SEPARATOR_VALUE_TYPE)
buffer.WriteString(val)
buffer.WriteRune(SEPARATOR_VALUES)
return
}
func (self *Serializer) encodeString(v PhpValue, left, right rune, isFinal bool) (buffer bytes.Buffer) {
val, _ := v.(string)
if isFinal {
buffer.WriteRune(TOKEN_STRING)
}
buffer.WriteString(self.prepareLen(len(val)))
buffer.WriteRune(left)
buffer.WriteString(val)
buffer.WriteRune(right)
if isFinal {
buffer.WriteRune(SEPARATOR_VALUES)
}
return
}
func (self *Serializer) encodeArray(v PhpValue, isFinal bool) (buffer bytes.Buffer) {
var (
arrLen int
s string
)
if isFinal {
buffer.WriteRune(TOKEN_ARRAY)
}
switch arrVal := v.(type) {
case PhpArray:
arrLen = len(arrVal)
buffer.WriteString(self.prepareLen(arrLen))
buffer.WriteRune(DELIMITER_OBJECT_LEFT)
for k, v := range arrVal {
s, _ = self.Encode(k)
buffer.WriteString(s)
s, _ = self.Encode(v)
buffer.WriteString(s)
}
case map[PhpValue]PhpValue:
arrLen = len(arrVal)
buffer.WriteString(self.prepareLen(arrLen))
buffer.WriteRune(DELIMITER_OBJECT_LEFT)
for k, v := range arrVal {
s, _ = self.Encode(k)
buffer.WriteString(s)
s, _ = self.Encode(v)
buffer.WriteString(s)
}
case PhpSlice:
arrLen = len(arrVal)
buffer.WriteString(self.prepareLen(arrLen))
buffer.WriteRune(DELIMITER_OBJECT_LEFT)
for k, v := range arrVal {
s, _ = self.Encode(k)
buffer.WriteString(s)
s, _ = self.Encode(v)
buffer.WriteString(s)
}
}
buffer.WriteRune(DELIMITER_OBJECT_RIGHT)
return
}
func (self *Serializer) encodeObject(v PhpValue) (buffer bytes.Buffer) {
obj, _ := v.(*PhpObject)
buffer.WriteRune(TOKEN_OBJECT)
buffer.WriteString(self.prepareClassName(obj.className))
encoded := self.encodeArray(obj.members, false)
buffer.WriteString(encoded.String())
return
}
func (self *Serializer) encodeSerialized(v PhpValue) (buffer bytes.Buffer) {
var serialized string
obj, _ := v.(*PhpObjectSerialized)
buffer.WriteRune(TOKEN_OBJECT_SERIALIZED)
buffer.WriteString(self.prepareClassName(obj.className))
if self.encodeFunc == nil {
serialized = obj.GetData()
} else {
var err error
if serialized, err = self.encodeFunc(obj.GetValue()); err != nil {
self.saveError(err)
}
}
encoded := self.encodeString(serialized, DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false)
buffer.WriteString(encoded.String())
return
}
func (self *Serializer) encodeSplArray(v PhpValue) bytes.Buffer {
var buffer bytes.Buffer
obj, _ := v.(*PhpSplArray)
buffer.WriteRune(TOKEN_SPL_ARRAY)
buffer.WriteRune(SEPARATOR_VALUE_TYPE)
encoded := self.encodeNumber(obj.flags)
buffer.WriteString(encoded.String())
data, _ := self.Encode(obj.array)
buffer.WriteString(data)
buffer.WriteRune(SEPARATOR_VALUES)
buffer.WriteRune(TOKEN_SPL_ARRAY_MEMBERS)
buffer.WriteRune(SEPARATOR_VALUE_TYPE)
data, _ = self.Encode(obj.properties)
buffer.WriteString(data)
return buffer
}
func (self *Serializer) prepareLen(l int) string {
return string(SEPARATOR_VALUE_TYPE) + strconv.Itoa(l) + string(SEPARATOR_VALUE_TYPE)
}
func (self *Serializer) prepareClassName(name string) string {
encoded := self.encodeString(name, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false)
return encoded.String()
}
func (self *Serializer) saveError(err error) {
if self.lastErr == nil {
self.lastErr = err
}
}