
本文详细讲解如何使用 php 根据用户在 html 表单中选择的尺寸、配料和饼底类型,准确计算并展示披萨订单总价,并安全写入日志文件。代码采用面向对象设计,具备可扩展性与健壮性。
本文详细讲解如何使用 php 根据用户在 html 表单中选择的尺寸、配料和饼底类型,准确计算并展示披萨订单总价,并安全写入日志文件。代码采用面向对象设计,具备可扩展性与健壮性。
在构建在线披萨订购系统时,后端需可靠地解析表单数据、执行价格逻辑、生成清晰反馈,并持久化订单信息。原始 pizza.php 脚本存在多个关键问题:未校验输入有效性、硬编码逻辑耦合度高、价格计算不完整(如未累加选中的多种配料)、文件写入无格式且易出错、缺少错误处理与类型安全。以下提供一套专业、可维护的解决方案。
✅ 推荐方案:面向对象的订单计算器(PHP 8+)
我们封装核心业务逻辑为 Pizza 类,将价格规则、数据映射与计算职责解耦,显著提升可读性与可测试性:
<?php class Pizza {
const FILE = 'pizza.txt';
// 配置常量:统一管理选项与价格映射
const SIZES = ['Large', 'X-Large'];
const TOPPINGS = ['Pepperoni', 'Mushroom'];
const CRUSTS = ['Thin', 'Original', 'DeepDish'];
const PRICE_TOPPINGS = 1.25;
const PRICE_SIZE = [9.95, 12.95];
const PRICE_CRUST = [0.00, 0.00, 2.00];
private ?string $customerName = null;
private array $chosenToppings = [];
private int $size = 0;
private int $crust = 0;
// 计算总金额(精确到小数点后两位)
public function calculatePizza(): float {
$costs = [
'size' => self::PRICE_SIZE[$this->size],
'crust' => self::PRICE_CRUST[$this->crust],
'toppings' => count($this->chosenToppings) * self::PRICE_TOPPINGS,
];
return round(array_sum($costs), 2); // 使用 round() 替代 number_format() 保持数值类型
}
// 安全写入订单日志(带分隔符与结构化字段)
public function saveToFile(): void {
$file = fopen(self::FILE, 'a');
if (!$file) {
throw new RuntimeException('无法打开订单日志文件: ' . self::FILE);
}
fwrite($file, "Size: " . self::SIZES[$this->size] . "\n");
foreach ($this->chosenToppings as $idx) {
fwrite($file, "Topping: " . self::TOPPINGS[$idx] . "\n");
}
fwrite($file, "Crust: " . self::CRUSTS[$this->crust] . "\n");
fwrite($file, "Name: " . ($this->customerName ?: 'Anonymous') . "\n");
fwrite($file, "Price: $" . $this->calculatePizza() . "\n");
fwrite($file, str_repeat('-', 40) . "\n");
fclose($file);
}
// 生成用户友好的感谢页面输出
public function getThankYou(): string {
$total = $this->calculatePizza();
$name = htmlspecialchars($this->customerName ?: 'Customer');
return "<h1>Pizza ordered, thank you {$name}!</h1><h1>Your total is \${$total}</h1>";
}
// 链式 setter 方法(支持灵活赋值)
public function setSize(string|int $value): self {
$key = is_int($value) ? $value : array_search($value, self::SIZES);
$this->size = $key !== false ? $key : 0;
return $this;
}
public function setCrust(string|int $value): self {
$key = is_int($value) ? $value : array_search($value, self::CRUSTS);
$this->crust = $key !== false ? $key : 0;
return $this;
}
public function addChosenToppings(array $values): self {
foreach ($values as $val) {
if (is_string($val) && in_array($val, self::TOPPINGS)) {
$this->chosenToppings[] = array_search($val, self::TOPPINGS);
}
}
return $this;
}
public function setCustomerName(?string $name): self {
$this->customerName = trim($name ?? '');
return $this;
}
}
// === 主程序入口 ===
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
try {
$pizza = new Pizza();
$pizza
->setSize($_POST['size'] ?? 'Large')
->addChosenToppings([
$_POST['pepperoni'] ?? null,
$_POST['mushrooms'] ?? null,
])
->setCrust($_POST['crust'] ?? 'Thin')
->setCustomerName($_POST['name'] ?? null)
->saveToFile();
echo $pizza->getThankYou();
} catch (Exception $e) {
http_response_code(500);
echo "<h1>Error processing order</h1><p>{$e->getMessage()}</p>";
}
} else {
include 'pizza.html'; // 显示表单页
}
⚠️ 关键注意事项与最佳实践
- 输入安全性:使用 htmlspecialchars() 转义客户姓名,防止 XSS;所有用户输入均经 array_search() 或类型校验后再使用,杜绝非法值导致的索引错误。
- 价格精度:round($sum, 2) 确保返回浮点数用于后续计算(如发票系统),而 number_format() 返回字符串,不适合参与运算。
- 文件操作健壮性:添加 fopen() 失败检查并抛出异常,避免静默失败;日志采用换行分隔、字段明确、末尾加横线分隔,便于人工或脚本解析。
- 表单兼容性:HTML 中 checkbox 未勾选时 $_POST 中不存在对应键,因此使用 ?? null 提供默认值,再由 addChosenToppings() 过滤空值。
- 扩展性设计:新增尺寸/配料只需扩展 const 数组及对应价格常量,无需修改计算逻辑;未来可轻松集成数据库、支付网关或折扣策略。
✅ 总结
该实现超越了基础功能需求,通过类封装、配置驱动、防御性编程和清晰的职责划分,构建了一个生产就绪的订单计算模块。它不仅正确处理了题设中的全部价格规则(大号 $9.95、超大号 $12.95、每种配料 $1.25、深盘饼底 +$2.00),更奠定了系统长期演进的基础——无论是增加素食选项、会员折扣,还是对接库存系统,都可在现有架构上平滑扩展。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











