本文详解如何使用php面向对象方式动态计算披萨订单总价,支持尺寸、配料和饼底类型组合计价,并将订单详情与金额安全写入文件,兼顾可维护性与健壮性。
本文详解如何使用
php
面向对象方式动态计算披萨订单总价,支持尺寸、配料和饼底类型组合计价,并将订单详情与金额安全写入文件,兼顾可维护性与健壮性。
在电商类表单处理中,准确、可扩展地计算商品总价是后端逻辑的核心能力之一。本文以披萨订单系统为例,展示如何用
现代PHP(≥8.0)面向对象设计
替代原始的流程式脚本,实现清晰、易测试、易维护的价格计算逻辑。
核心设计思路
我们封装一个 Pizza 类,将业务规则(价格映射、选项校验、金额聚合)与数据操作(文件写入、响应输出)解耦。关键优势包括:
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
✅
配置集中化
:尺寸、饼底、配料及其单价统一定义为常量,便于后续调整;
✅
类型安全与容错
:自动处理空值、非法选项(如未选饼底),默认回退至合理值;
✅
灵活扩展性
:新增配料或尺寸只需修改常量数组,无需重写计算逻辑;
✅
职责分离
:calculatePizza() 专注金额计算,saveToFile() 专注持久化,getThankYou() 专注响应渲染。
完整实现代码(pizza.php)
关键注意事项与最佳实践
安全性提醒
:本例未包含XSS防护与SQL注入防御(因无数据库),实际生产环境需对 $_POST 数据进行 htmlspecialchars() 转义后再输出;
文件写入风险
:fopen(..., 'a') 在高并发下可能产生竞态条件,建议改用数据库或添加文件锁(flock());
表单兼容性
:HTML中
self::PRICE_SIZE[$this->getSize()],
'crust' => self::PRICE_CRUST[$this->getCrust()],
'toppings' => count($this->getChosenToppings()) * self::PRICE_TOPPINGS,
];
return (float) number_format(array_sum($costs), 2);
}
/**
* 将订单信息追加写入 pizza.txt(含格式化分隔)
*/
public function saveToFile(): self {
$file = fopen(self::FILE, 'a') or die('Unable to open pizza.txt for writing!');
fwrite($file, "Size: " . self::SIZES[$this->getSize()] . "\n");
foreach ($this->getChosenToppings() as $idx) {
fwrite($file, "Topping: " . self::TOPPINGS[$idx] . "\n");
}
fwrite($file, "Crust: " . self::CRUSTS[$this->getCrust()] . "\n");
fwrite($file, "Name: " . ($this->getCustomerName() ?: 'Unknown') . "\n");
fwrite($file, "Price: $" . $this->calculatePizza() . "\n");
fwrite($file, str_repeat('-', 40) . "\n");
fclose($file);
return $this;
}
/**
* 生成并输出感谢页面(含客户名与总价)
*/
public function getThankYou(bool $echo = true): string {
$html = <<Pizza ordered, thank you {$this->getCustomerName()}!
Your total is \${$this->calculatePizza()}
HTML;
if ($echo) echo $html;
return $html;
}
// --- 属性设置器(支持字符串/数字输入,自动标准化)---
public function setSize(string|int|null $size): self {
if (is_string($size) && in_array($size, self::SIZES)) {
$this->size = array_search($size, self::SIZES);
} elseif (is_int($size) && isset(self::SIZES[$size])) {
$this->size = $size;
} else {
$this->size = 0; // 默认 Large
}
return $this;
}
public function setCrust(string|int|null $crust): self {
if (is_string($crust) && in_array($crust, self::CRUSTS)) {
$this->crust = array_search($crust, self::CRUSTS);
} elseif (is_int($crust) && isset(self::CRUSTS[$crust])) {
$this->crust = $crust;
} else {
$this->crust = 0; // 默认 Thin
}
return $this;
}
public function addChosenToppings(array $toppings): self {
foreach ($toppings as $topping) {
if (empty($topping)) continue;
if (is_string($topping) && in_array($topping, self::TOPPINGS)) {
$this->chosenToppings[] = array_search($topping, self::TOPPINGS);
} elseif (is_int($topping) && isset(self::TOPPINGS[$topping])) {
$this->chosenToppings[] = $topping;
}
}
return $this;
}
public function setCustomerName(?string $name): self {
$this->customerName = trim($name) ?: 'Unknown';
return $this;
}
// --- 获取器 ---
public function getCustomerName(): ?string { return $this->customerName; }
public function getChosenToppings(): array { return $this->chosenToppings; }
public function getSize(): int { return $this->size ?? 0; }
public function getCrust(): int { return $this->crust ?? 0; }
}
// === 请求处理入口 ===
if ($_POST['submit'] ?? false) {
$pizza = new Pizza();
$pizza
->setSize($_POST['size'] ?? null)
->setCrust($_POST['crust'] ?? null)
->addChosenToppings([
$_POST['pepperoni'] ?? null,
$_POST['mushrooms'] ?? null,
])
->setCustomerName($_POST['name'] ?? null)
->saveToFile()
->getThankYou();
} else {
include 'pizza.html'; // 未提交时显示表单
}