*/ public function parameters(): array { return [ 'type' => 'object', 'properties' => [ 'path' => [ 'type' => 'string', 'description' => '要读取的文件路径(相对或绝对路径)。', ], 'start_line' => [ 'type' => 'integer', 'description' => '起始行号(从1开始),默认从第一行开始。', 'minimum' => 1, 'default' => 1, ], 'end_line' => [ 'type' => 'integer', 'description' => '结束行号(包含),默认读取到文件末尾。', 'minimum' => 1, ], 'max_size' => [ 'type' => 'integer', 'description' => '最大读取字节数(1-10MB),默认1MB,防止读取过大文件。', 'minimum' => 1, 'maximum' => 10485760, 'default' => 1048576, ], 'encoding' => [ 'type' => 'string', 'description' => '文件编码,默认UTF-8。', 'enum' => ['UTF-8', 'GBK', 'GB2312', 'ISO-8859-1'], 'default' => 'UTF-8', ], ], 'required' => ['path'], ]; } /** * @param array $arguments * @return array */ public function execute(array $arguments): array { $path = $arguments['path'] ?? ''; // 验证路径 if (empty($path)) { throw new InvalidArgumentException('文件路径不能为空。'); } // 安全检查:防止路径遍历攻击 $realPath = realpath($path); if ($realPath === false) { throw new InvalidArgumentException("文件不存在:{$path}"); } if (!is_file($realPath)) { throw new InvalidArgumentException("路径不是文件:{$path}"); } if (!is_readable($realPath)) { throw new InvalidArgumentException("文件不可读:{$path}"); } // 获取参数 $startLine = max(1, (int)($arguments['start_line'] ?? 1)); $endLine = isset($arguments['end_line']) ? max(1, (int)$arguments['end_line']) : null; $maxSize = min(10485760, max(1, (int)($arguments['max_size'] ?? 1048576))); $encoding = $arguments['encoding'] ?? 'UTF-8'; // 检查文件大小 $fileSize = filesize($realPath); if ($fileSize === false) { throw new InvalidArgumentException("无法获取文件大小:{$path}"); } return $this->readFileContent($realPath, $startLine, $endLine, $maxSize, $encoding, $fileSize); } /** * 读取文件内容 * * @param string $path * @param int $startLine * @param int|null $endLine * @param int $maxSize * @param string $encoding * @param int $fileSize * @return array */ private function readFileContent( string $path, int $startLine, ?int $endLine, int $maxSize, string $encoding, int $fileSize ): array { $result = [ 'path' => $path, 'size' => $fileSize, 'encoding' => $encoding, ]; // 如果文件为空 if ($fileSize === 0) { $result['content'] = ''; $result['lines_read'] = 0; $result['truncated'] = false; return $result; } // 读取文件 $handle = fopen($path, 'r'); if ($handle === false) { throw new InvalidArgumentException("无法打开文件:{$path}"); } try { return $this->readLines($handle, $startLine, $endLine, $maxSize, $encoding, $result); } finally { fclose($handle); } } /** * 按行读取文件 * * @param resource $handle * @param int $startLine * @param int|null $endLine * @param int $maxSize * @param string $encoding * @param array $result * @return array */ private function readLines( $handle, int $startLine, ?int $endLine, int $maxSize, string $encoding, array $result ): array { $lines = []; $currentLine = 0; $bytesRead = 0; $truncated = false; while (($line = fgets($handle)) !== false) { $currentLine++; // 跳过起始行之前的内容 if ($currentLine < $startLine) { continue; } // 检查是否超过结束行 if ($endLine !== null && $currentLine > $endLine) { break; } // 检查大小限制 $lineLength = strlen($line); if ($bytesRead + $lineLength > $maxSize) { $truncated = true; break; } $lines[] = $line; $bytesRead += $lineLength; } $content = implode('', $lines); // 编码转换 if ($encoding !== 'UTF-8' && function_exists('mb_convert_encoding')) { $content = mb_convert_encoding($content, 'UTF-8', $encoding); } $result['content'] = $content; $result['lines_read'] = count($lines); $result['start_line'] = $startLine; $result['end_line'] = $endLine ?? $currentLine; $result['truncated'] = $truncated; $result['bytes_read'] = $bytesRead; if ($truncated) { $result['warning'] = "内容已截断,已读取 {$bytesRead} 字节(限制:{$maxSize} 字节)"; } return $result; } }