Files
lone-services/utils/phpValue.go
T
2026-08-05 17:37:32 +08:00

318 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"fmt"
"strconv"
"strings"
)
// PHPValue 定义 PHP 序列化值的类型(兼容字符串、整数、数组)
type PHPValue interface{}
// PHPDecode 原生解析 PHP 序列化字符串为 map[string]PHPValue
func PHPDecode(serialized string) (map[string]PHPValue, error) {
// 预处理:去除首尾空白
serialized = strings.TrimSpace(serialized)
// 验证是否为 PHP 数组格式(a:N:{...}
if !strings.HasPrefix(serialized, "a:") {
return nil, fmt.Errorf("不是 PHP 数组格式")
}
// 提取数组内容(去掉外层 a:N:{ 和 末尾的 })
colonIdx := strings.Index(serialized, ":")
if colonIdx == -1 {
return nil, fmt.Errorf("格式错误:缺少冒号")
}
braceOpenIdx := strings.Index(serialized, "{")
if braceOpenIdx == -1 {
return nil, fmt.Errorf("格式错误:缺少左大括号")
}
content := serialized[braceOpenIdx+1 : len(serialized)-1]
// 递归解析数组内容
result := make(map[string]PHPValue)
idx := 0
contentLen := len(content)
for idx < contentLen {
// 跳过空白字符
for idx < contentLen && (content[idx] == ' ' || content[idx] == '\t' || content[idx] == '\n') {
idx++
}
if idx >= contentLen {
break
}
// 解析 Key
key, keyEndIdx, err := parsePHPValueAt(content, idx)
if err != nil {
return nil, fmt.Errorf("解析 Key 失败:%v", err)
}
idx = keyEndIdx + 1 // 跳过 Key 后的分号
// 解析 Value
val, valEndIdx, err := parsePHPValueAt(content, idx)
if err != nil {
return nil, fmt.Errorf("解析 Value 失败:%v", err)
}
idx = valEndIdx + 1 // 跳过 Value 后的分号
// 将 Key 转为字符串并存入结果
var keyStr string
switch k := key.(type) {
case string:
keyStr = k
case int:
keyStr = strconv.Itoa(k)
default:
keyStr = fmt.Sprintf("%v", k)
}
result[keyStr] = val
}
return result, nil
}
// parsePHPValueAt 从指定索引解析单个 PHP 序列化值
func parsePHPValueAt(s string, start int) (PHPValue, int, error) {
if start >= len(s) {
return nil, -1, fmt.Errorf("索引越界")
}
// 根据类型前缀解析
switch s[start] {
case 's': // 字符串类型:s:长度:"内容";
return parsePHPString(s, start)
case 'i': // 整数类型:i:数值;
return parsePHPInt(s, start)
case 'a': // 数组类型:a:长度:{...};
return parsePHPArray(s, start)
default:
return nil, -1, fmt.Errorf("不支持的类型:%c", s[start])
}
}
// parsePHPString 解析 PHP 字符串类型(s:len:"value"
func parsePHPString(s string, start int) (string, int, error) {
// 格式:s:3:"abc";
// 1. 跳过 "s:"
if len(s) < start+2 || s[start:start+2] != "s:" {
return "", -1, fmt.Errorf("不是字符串类型")
}
idx := start + 2
// 2. 解析长度
lenStr := ""
for idx < len(s) && s[idx] != ':' {
lenStr += string(s[idx])
idx++
}
if idx >= len(s) || s[idx] != ':' {
return "", -1, fmt.Errorf("字符串长度解析失败")
}
strLen, err := strconv.Atoi(lenStr)
if err != nil {
return "", -1, fmt.Errorf("长度不是数字:%v", err)
}
idx++ // 跳过冒号
// 3. 跳过引号
if idx >= len(s) || s[idx] != '"' {
return "", -1, fmt.Errorf("缺少字符串起始引号")
}
idx++
// 4. 提取字符串内容
endIdx := idx + strLen
if endIdx > len(s) {
return "", -1, fmt.Errorf("字符串长度不足")
}
strVal := s[idx:endIdx]
idx = endIdx
// 5. 跳过结束引号和分号
if idx >= len(s) || s[idx] != '"' {
return "", -1, fmt.Errorf("缺少字符串结束引号")
}
idx++
if idx >= len(s) || s[idx] != ';' {
return "", -1, fmt.Errorf("缺少字符串结束分号")
}
return strVal, idx, nil
}
// parsePHPInt 解析 PHP 整数类型(i:123;
func parsePHPInt(s string, start int) (int, int, error) {
// 格式:i:123;
// 1. 跳过 "i:"
if len(s) < start+2 || s[start:start+2] != "i:" {
return 0, -1, fmt.Errorf("不是整数类型")
}
idx := start + 2
// 2. 解析数字
numStr := ""
for idx < len(s) && s[idx] != ';' {
numStr += string(s[idx])
idx++
}
if numStr == "" {
return 0, -1, fmt.Errorf("整数为空")
}
numVal, err := strconv.Atoi(numStr)
if err != nil {
return 0, -1, fmt.Errorf("整数解析失败:%v", err)
}
return numVal, idx, nil
}
// parsePHPArray 解析 PHP 数组类型(a:len:{...}
func parsePHPArray(s string, start int) (map[string]PHPValue, int, error) {
// 格式:a:2:{s:1:"k";i:123;s:1:"v";s:3:"abc";};
// 1. 跳过 "a:"
if len(s) < start+2 || s[start:start+2] != "a:" {
return nil, -1, fmt.Errorf("不是数组类型")
}
idx := start + 2
// 2. 解析数组长度(仅校验,实际按内容解析)
for idx < len(s) && s[idx] != ':' {
idx++
}
if idx >= len(s) || s[idx] != ':' {
return nil, -1, fmt.Errorf("数组长度解析失败")
}
idx++
// 3. 找到数组起始大括号
if idx >= len(s) || s[idx] != '{' {
return nil, -1, fmt.Errorf("缺少数组起始大括号")
}
idx++
// 4. 解析数组内容(递归调用 PHPDecode 的核心逻辑)
arrayContentStart := idx
depth := 1
arrayContentEnd := -1
// 找到匹配的结束大括号(处理嵌套数组)
for idx < len(s) && depth > 0 {
switch s[idx] {
case '{':
depth++
case '}':
depth--
if depth == 0 {
arrayContentEnd = idx
}
}
idx++
}
if arrayContentEnd == -1 {
return nil, -1, fmt.Errorf("数组缺少结束大括号")
}
// 5. 解析数组内容为 map
arrayContent := s[arrayContentStart:arrayContentEnd]
arrayResult := make(map[string]PHPValue)
innerIdx := 0
contentLen := len(arrayContent)
for innerIdx < contentLen {
// 跳过空白
for innerIdx < contentLen && (arrayContent[innerIdx] == ' ' || arrayContent[innerIdx] == '\t') {
innerIdx++
}
if innerIdx >= contentLen {
break
}
// 解析 Key
key, keyEnd, err := parsePHPValueAt(arrayContent, innerIdx)
if err != nil {
return nil, -1, fmt.Errorf("解析数组 Key 失败:%v", err)
}
innerIdx = keyEnd + 1 // 跳过分号
// 解析 Value
val, valEnd, err := parsePHPValueAt(arrayContent, innerIdx)
if err != nil {
return nil, -1, fmt.Errorf("解析数组 Value 失败:%v", err)
}
innerIdx = valEnd + 1 // 跳过分号
// 转换 Key 为字符串
var keyStr string
switch k := key.(type) {
case string:
keyStr = k
case int:
keyStr = strconv.Itoa(k)
default:
keyStr = fmt.Sprintf("%v", k)
}
arrayResult[keyStr] = val
}
// 6. 跳过数组结束后的分号
if idx < len(s) && s[idx] == ';' {
idx++
}
return arrayResult, idx - 1, nil // 返回数组结束的索引
}
// GetInt 从 map 中安全获取 int 类型值,失败返回默认值
func GetInt(m map[string]PHPValue, key string, defaultValue int) int {
val, ok := m[key]
if !ok {
return defaultValue
}
// 类型断言:将 interface{} 转为 int
num, ok := val.(int)
if !ok {
fmt.Printf("警告:%s 不是 int 类型,使用默认值 %d\n", key, defaultValue)
return defaultValue
}
return num
}
// GetString 从 map 中安全获取 string 类型值,失败返回默认值
func GetString(m map[string]PHPValue, key string, defaultValue string) string {
val, ok := m[key]
if !ok {
return defaultValue
}
// 类型断言:将 interface{} 转为 string
str, ok := val.(string)
if !ok {
fmt.Printf("警告:%s 不是 string 类型,使用默认值 %s\n", key, defaultValue)
return defaultValue
}
return str
}
// GetMap 从 map 中安全获取嵌套 map 类型值,失败返回空 map
func GetMap(m map[string]PHPValue, key string) map[string]interface{} {
val, ok := m[key]
if !ok {
return make(map[string]interface{})
}
// 类型断言:将 interface{} 转为 map[string]interface{}
nestedMap, ok := val.(map[string]PHPValue)
if !ok {
fmt.Printf("警告:%s 不是数组类型\n", key)
return make(map[string]interface{})
}
// 转换为通用 map[string]interface{}
result := make(map[string]interface{})
for k, v := range nestedMap {
result[k] = v
}
return result
}