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
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 |
|
} |
|
}
|
|
|