errors = array(); /* 解析原始JSON */ $data = json_decode($json, true); if(json_last_error() !== JSON_ERROR_NONE) { $this->errors[] = "JSON解析错误: " . json_last_error_msg(); return false; } /* 解析查询语句结构 */ $queryStructure = $this->parseQuery($query); if($queryStructure === false) return false; /* 提取字段并应用类型转换 */ $result = $this->extractFields($data, $queryStructure); /* 返回格式化JSON */ return json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } /** * 解析查询字符串,生成查询结构数组 * @param string $query 查询字符串 * @return array|bool 查询结构数组,失败返回false */ private function parseQuery($query) { $query = trim($query); if (empty($query)) { $this->errors[] = "empty"; return false; } try { return $this->parseExpression($query, 0)[0]; } catch(Exception $e) { $this->errors[] = "error: " . $e->getMessage(); return false; } } /** * 递归解析表达式(处理嵌套结构) * @param string $query 查询字符串 * @param int $start 起始解析位置 * @return array [查询结构数组, 下一个解析位置] * @throws Exception 语法错误时抛出异常 */ private function parseExpression($query, $start) { $result = []; $i = $start; $len = strlen($query); while($i < $len) { /* 跳过空白字符 */ while($i < $len && in_array($query[$i], [' ', "\t", "\n", "\r"])) $i++; if($i >= $len) break; /* 遇到闭合符,退出当前嵌套层级 */ if($query[$i] === '}') break; /* 解析字段名、类型、别名、数组索引 */ $fieldInfo = $this->parseFieldName($query, $i); $fieldName = $fieldInfo['name']; $fieldAlias = $fieldInfo['alias']; $arrayIndex = $fieldInfo['index']; $targetType = $fieldInfo['target_type']; $i = $fieldInfo['pos']; /* 跳过空白字符 */ while($i < $len && in_array($query[$i], [' ', "\t", "\n", "\r"])) $i++; /* 处理嵌套结构(如 parent:{child}) */ if($i < $len && $query[$i] === ':') { $i++; // 跳过冒号 /* 跳过空白字符 */ while($i < $len && in_array($query[$i], [' ', "\t", "\n", "\r"])) $i++; if($i < $len && $query[$i] === '{') { $i++; // 跳过左大括号 /* 递归解析嵌套字段 */ list($nestedResult, $newPos) = $this->parseExpression($query, $i); $i = $newPos; /* 检查是否有闭合符 */ if($i < $len && $query[$i] === '}') { $i++; // 跳过右大括号 } else { throw new Exception("缺少闭合符 '}'"); } /* 存储嵌套字段结构(包含类型转换信息)*/ $result[$fieldName] = array( 'type' => 'nested', 'alias' => $fieldAlias, 'index' => $arrayIndex, 'target_type' => $targetType, 'fields' => $nestedResult ); } else { throw new Exception("'{' afer ':'"); } } else { /* 存储简单字段结构(包含类型转换信息)*/ $result[$fieldName] = array( 'type' => 'simple', 'alias' => $fieldAlias, 'index' => $arrayIndex, 'target_type' => $targetType ); } /* 跳过空白字符 */ while($i < $len && in_array($query[$i], [' ', "\t", "\n", "\r"])) $i++; /* 处理字段分隔符 */ if($i < $len && $query[$i] === ',') $i++; // 跳过逗号 } return [$result, $i]; } /** * 解析字段名、类型转换、别名、数组索引 * @param string $query 查询字符串 * @param int $start 起始解析位置 * @return array 包含字段信息的数组 * @throws Exception 语法错误时抛出异常 */ private function parseFieldName($query, $start) { $i = $start; $len = strlen($query); $name = ''; $alias = null; $index = null; $targetType = null; /* 1. 读取字段名(可能包含类型转换,如 user(array))*/ while($i < $len && !in_array($query[$i], [':', ',', '}', '[', '|', ' ', "\t", "\n", "\r"])) { $name .= $query[$i]; $i++; } /* 2. 解析类型转换(格式:字段名(类型))*/ $typePattern = '/^(.+)\((\w+)\)$/'; if(preg_match($typePattern, $name, $matches)) { $name = trim($matches[1]); $targetType = trim($matches[2]); /* 验证支持的类型 */ $allowedTypes = array('array', 'int', 'float', 'bool', 'string'); if(!in_array($targetType, $allowedTypes)) { throw new Exception("Not support '{$targetType}', only " . implode(',', $allowedTypes)); } } /* 3. 解析别名(格式:字段名|别名)*/ if($i < $len && $query[$i] === '|') { $i++; // 跳过竖线 $alias = ''; while($i < $len && !in_array($query[$i], [':', ',', '}', '[', ' ', "\t", "\n", "\r"])) { $alias .= $query[$i]; $i++; } $alias = trim($alias); /* 别名不能为空 */ if(empty($alias)) throw new Exception("alias is empty"); } /* 4. 解析数组索引(格式:字段名[索引])*/ if($i < $len && $query[$i] === '[') { $i++; // 跳过左中括号 $indexStr = ''; while($i < $len && $query[$i] !== ']') { $indexStr .= $query[$i]; $i++; } /* 检查是否有闭合符 */ if($i < $len && $query[$i] === ']') { $i++; // 跳过右中括号 /* 处理通配符索引和数字索引 */ if($indexStr === '*') { $index = '*'; } elseif(is_numeric($indexStr)) { $index = intval($indexStr); } else { throw new Exception("index error '{$indexStr}', only support number or '*'"); } } else { throw new Exception("need ']'"); } } /* 字段名不能为空 */ if(empty($name)) throw new Exception("field is empty"); return array( 'name' => $name, 'alias' => $alias, 'index' => $index, 'target_type' => $targetType, 'pos' => $i ); } /** * 提取字段并应用类型转换 * @param array|object $data 原始数据(数组或对象) * @param array $queryStructure 查询结构数组 * @return array 提取并转换后的结果数组 */ private function extractFields($data, $queryStructure) { /* 非数组结构直接返回(避免后续报错)*/ if(!is_array($queryStructure)) return $this->convertValueType($data, null); if(!is_array($data) && !is_object($data)) return null; $result = array(); $dataArray = (array)$data; foreach($queryStructure as $field => $config) { $resultKey = $config['alias'] ?? $field; // 优先使用别名 $targetType = $config['target_type'] ?? null; // 目标转换类型 /* 1. 处理通配符字段(如 *:{name})*/ if($field === '*') { foreach($dataArray as $key => $value) { if($config['type'] === 'simple') { /* 简单通配符字段:直接转换值 */ $result[$key] = $this->convertValueType($value, $targetType); } elseif($config['type'] === 'nested') { /* 嵌套通配符字段:先提取嵌套字段,再转换 */ $nestedValue = $this->extractFields($value, $config['fields']); $result[$key] = $this->convertValueType($nestedValue, $targetType); } } continue; } /* 字段不存在时跳过 */ if(!array_key_exists($field, $dataArray)) continue; $fieldData = $dataArray[$field]; /* 2. 处理数组索引(如 users[0]、users[*])*/ if($config['index'] !== null) { /* 非数组类型跳过 */ if(!is_array($fieldData)) continue; /* 2.1 通配符索引(提取所有数组元素)*/ if($config['index'] === '*') { $extractedArray = array(); foreach($fieldData as $item) { /* 提取元素值(简单/嵌套)*/ $itemValue = $config['type'] === 'simple' ? $item : $this->extractFields($item, $config['fields']); /* 应用类型转换 */ $extractedArray[] = $this->convertValueType($itemValue, $targetType); } $result[$resultKey] = $extractedArray; } /* 2.2 特定数字索引(提取指定位置元素)*/ elseif(isset($fieldData[$config['index']])) { $item = $fieldData[$config['index']]; /* 提取元素值(简单/嵌套)*/ $itemValue = $config['type'] === 'simple' ? $item : $this->extractFields($item, $config['fields']); /* 应用类型转换并保留索引*/ $result[$resultKey] = array( $config['index'] => $this->convertValueType($itemValue, $targetType) ); } } /* 3. 无数组索引(普通字段)*/ else { /* 提取字段值(简单/嵌套)*/ $fieldValue = $config['type'] === 'simple' ? $fieldData : $this->extractFields($fieldData, $config['fields']); /* 应用类型转换 */ $result[$resultKey] = $this->convertValueType($fieldValue, $targetType); } } return $result; } /** * 按目标类型转换值 * @param mixed $value 原始值 * @param string|null $targetType 目标类型(array/int/float/bool/string) * @return mixed 转换后的值 */ private function convertValueType($value, ?string $targetType) { /* 无目标类型时返回原始值 */ if($targetType === null) return $value; switch($targetType) { /* 转换为数组:关联数组→索引数组,非数组→包装为数组 */ case 'array': if(is_array($value)) return array_values($value); return [$value]; /* 转换为整数:仅对数值/数值字符串有效 */ case 'int': return is_numeric($value) ? intval($value) : $value; /* 转换为浮点数:仅对数值/数值字符串有效 */ case 'float': return is_numeric($value) ? floatval($value) : $value; /* 转换为布尔值:处理字符串"true"/"false"和数字1/0 */ case 'bool': if(is_string($value)) { $lowerValue = strtolower($value); return $lowerValue === 'true' || $lowerValue === '1'; } return boolval($value); /* 转换为字符串:直接强制转换 */ case 'string': return strval($value); /* 未知类型返回原始值 */ default: return $value; } } /** * 获取错误信息列表 * @return string[] 错误信息数组 */ public function getErrors() { return $this->errors; } }