
本文介绍如何将臃肿的 exportFile() 函数按单一职责原则拆解为独立、可复用、易测试的小函数,涵盖文件操作、数据获取、平台专属格式化等模块,并提供结构清晰的重构示例与关键注意事项。
本文介绍如何将臃肿的 `exportfile()` 函数按单一职责原则拆解为独立、可复用、易测试的小函数,涵盖文件操作、数据获取、平台专属格式化等模块,并提供结构清晰的重构示例与关键注意事项。
将一个承担多重职责的大型函数(如 exportFile)直接维护不仅容易出错,还严重阻碍可读性、单元测试和后续扩展。遵循 单一职责原则(SRP) 和 关注点分离(SoC),我们应将其拆分为职责明确、命名清晰、彼此解耦的小函数。以下是以实际导出逻辑为基础的专业重构方案:
✅ 核心拆分策略
- openExportFile():专注文件打开与错误处理;
- fetchExportData():封装数据库连接与查询逻辑,返回标准化数组;
- generateHeader($platform):根据平台返回对应 CSV 表头;
- transformRow($row, $platform):将原始数据行映射为平台特定字段顺序与格式(如类型转换、空值处理);
- writeCsv($file, $header, $rows):统一执行写入操作,复用 fputcsv。
?️ 重构后完整示例(含健壮性增强)
function openExportFile(string $fileName): resource
{
$filePath = DOWNLOAD_FILE_PATH . $fileName;
$file = fopen($filePath, 'wb');
if (!$file) {
throw new RuntimeException(json_encode([
'status' => 'error',
'errMessage' => "Failed to open export file: {$filePath}"
]));
}
return $file;
}
function fetchExportData(): array
{
$db = new MysqliDb(TEST_DB_HOSTNAME, TEST_DB_USERNAME, TEST_DB_PASSWORD, TEST_DB_DATABASE);
return $db->rawQuery("SELECT * FROM database");
}
function generateHeader(string $platform): array
{
return match ($platform) {
'magento' => ['sku', 'name', 'price', 'category'],
'catch' => ['item_id', 'title', 'retail_price', 'department'],
'amazon' => ['asin', 'product_name', 'list_price', 'brand'],
default => throw new InvalidArgumentException("Unsupported platform: {$platform}")
};
}
function transformRow(array $row, string $platform): array
{
return match ($platform) {
'magento' => [
$row['product_sku'] ?? '',
$row['product_name'] ?? '',
(float)($row['price'] ?? 0),
$row['category_path'] ?? ''
],
'catch' => [
$row['id'] ?? 0,
$row['title'] ?? '',
round((float)($row['msrp'] ?? 0), 2),
$row['dept'] ?? 'Uncategorized'
],
'amazon' => [
$row['asin'] ?? '',
$row['name'] ?? '',
number_format((float)($row['price'] ?? 0), 2),
$row['brand'] ?? ''
],
default => throw new InvalidArgumentException("Unsupported platform: {$platform}")
};
}
function writeCsv(resource $file, array $header, array $rows): void
{
fputcsv($file, $header);
foreach ($rows as $row) {
fputcsv($file, $row);
}
}
// 主函数:仅协调流程,无业务逻辑
function exportFile(string $fileName, string $platform, ?string $option = null): void
{
$file = openExportFile($fileName);
try {
$data = fetchExportData();
$header = generateHeader($platform);
$csvRows = array_map(fn($row) => transformRow($row, $platform), $data);
writeCsv($file, $header, $csvRows);
fclose($file);
} catch (Exception $e) {
fclose($file);
throw $e;
}
}⚠️ 关键注意事项
- 避免全局依赖:MysqliDb 实例应在 fetchExportData() 内创建并销毁,或通过依赖注入传入(更推荐用于测试);
- 错误处理升级:使用 try/catch 替代 die(),便于上层捕获与日志记录;
- 类型安全:PHP 8+ 推荐启用严格模式,参数与返回类型标注(如 string, array, resource);
- 平台扩展性:新增平台只需扩展 match 分支,无需修改主流程;
- 性能优化:大数据量时,考虑流式写入(已实现)+ 禁用自动内存限制(ini_set('memory_limit', '-1'));
- 测试友好:每个小函数均可独立单元测试(如 transformRow() 可验证不同平台输出)。
通过此重构,代码可维护性显著提升:修改 Magento 字段映射不再影响 Catch 导出逻辑;添加 Amazon 支持只需补充两处 match 分支;单元测试覆盖率可轻松达 90%+。这才是真正面向演进的 PHP 工程实践。



















