6.4 字符串格式化
字符串格式化是将数据按照特定格式转换为字符串的过程,这在Web开发中非常重要。无论是显示价格、格式化日期、生成报告,还是创建用户友好的输出,都离不开字符串格式化技术。
格式化的重要性和应用场景
在Web开发中,字符串格式化广泛应用于:
- 数据显示:将数字、日期等按照本地化格式显示
- 报表生成:创建格式化的表格和报告
- 日志记录:生成结构化的日志信息
- 用户界面:创建友好的用户界面文本
- 数据导出:生成CSV、XML等格式化文件
- 模板系统:动态生成HTML、邮件等
数字格式化
number_format() - 基本数字格式化
<?php
// 基本用法:添加千位分隔符
$number = 1234567.89;
$formatted = number_format($number);
echo "格式化: {$formatted}\n"; // 输出: 1,234,568
// 指定小数位数
$number = 1234.567;
$formatted = number_format($number, 2);
echo "两位小数: {$formatted}\n"; // 输出: 1,234.57
// 指定小数点符号和千位分隔符
$number = 1234.56;
$formatted = number_format($number, 2, ',', '.'); // 德国格式
echo "德国格式: {$formatted}\n"; // 输出: 1.234,56
$formatted = number_format($number, 2, '.', ','); // 美国格式
echo "美国格式: {$formatted}\n"; // 输出: 1,234.56
// 实用函数:格式化货币显示
function formatCurrency($amount, $currency = '¥', $locale = 'zh_CN') {
switch ($locale) {
case 'zh_CN':
return $currency . number_format($amount, 2);
case 'en_US':
return $currency . number_format($amount, 2, '.', ',');
case 'de_DE':
return number_format($amount, 2, ',', '.') . ' ' . $currency;
default:
return $currency . number_format($amount, 2);
}
}
echo "中文货币: " . formatCurrency(1234.56, '¥', 'zh_CN') . "\n";
echo "美元货币: " . formatCurrency(1234.56, '$', 'en_US') . "\n";
echo "欧元货币: " . formatCurrency(1234.56, '€', 'de_DE') . "\n";
?>
printf() 和 sprintf() - 格式化输出
<?php
// printf() - 直接输出
$name = "张三";
$age = 25;
$salary = 5000.50;
printf("姓名:%s,年龄:%d岁,工资:%.2f元\n", $name, $age, $salary);
// sprintf() - 返回格式化字符串
$formatted = sprintf("姓名:%s,年龄:%d岁,工资:%.2f元", $name, $age, $salary);
echo "格式化字符串: {$formatted}\n";
// 常用格式说明符
$number = 123.456;
$integer = 42;
$string = "Hello";
$hex = 255;
printf("二进制: %b\n", $hex); // 二进制
printf("字符: %c\n", 65); // ASCII字符
printf("十进制: %d\n", $integer); // 十进制整数
printf("科学计数: %e\n", $number); // 科学计数法
printf("浮点数: %f\n", $number); // 浮点数
printf八进制: %o\n", $hex); // 八进制
printf("字符串: %s\n", $string); // 字符串
printf("十六进制: %x\n", $hex); // 十六进制(小写)
printf("十六进制: %X\n", $hex); // 十六进制(大写)
printf("百分号: %%\n"); // 百分号
// 格式化对齐和填充
printf("%10s | %-10s | %8.2f\n", "产品", "数量", "价格");
printf("%10s | %-10s | %8.2f\n", "苹果", "100", "5.99");
printf("%10s | %-10s | %8.2f\n", "香蕉", "50", "3.50");
printf("%10s | %-10s | %8.2f\n", "橙子", "75", "4.25");
?>
vprintf() 和 vsprintf() - 可变参数格式化
<?php
// vprintf() - 可变参数直接输出
$args = ["张三", 25, "工程师"];
vprintf("姓名:%s,年龄:%d,职业:%s\n", $args);
// vsprintf() - 可变参数返回字符串
$formatted = vsprintf("姓名:%s,年龄:%d,职业:%s", $args);
echo "格式化结果: {$formatted}\n";
// 实际应用:动态格式化表格数据
function formatTableRow($format, ...$data) {
return vsprintf($format, $data);
}
$products = [
["笔记本电脑", 2, 4999.99],
["无线鼠标", 5, 89.90],
["USB键盘", 3, 159.00]
];
echo str_repeat("-", 50) . "\n";
echo formatTableRow("%-15s | %5s | %10s\n", "产品名称", "数量", "价格");
echo str_repeat("-", 50) . "\n";
foreach ($products as $product) {
echo formatTableRow("%-15s | %5d | %10.2f\n", ...$product);
}
echo str_repeat("-", 50) . "\n";
// 创建可重用的格式化函数
function createFormatter($template) {
return function(...$args) use ($template) {
return vsprintf($template, $args);
};
}
$userFormatter = createFormatter("用户:%s,邮箱:%s,注册时间:%s");
echo $userFormatter("张三", "zhangsan@example.com", "2024-01-15") . "\n";
echo $userFormatter("李四", "lisi@example.org", "2024-02-20") . "\n";
?>
字符串填充和对齐
str_pad() - 字符串填充
<?php
// 基本填充
$text = "PHP";
$padded = str_pad($text, 10, " ", STR_PAD_RIGHT);
echo "右填充: '{$padded}'\n"; // 输出: 'PHP '
$padded = str_pad($text, 10, " ", STR_PAD_LEFT);
echo "左填充: '{$padded}'\n"; // 输出: ' PHP'
$padded = str_pad($text, 10, " ", STR_PAD_BOTH);
echo "两边填充: '{$padded}'\n"; // 输出: ' PHP '
// 使用自定义填充字符
$padded = str_pad($text, 10, "*", STR_PAD_RIGHT);
echo "自定义填充: '{$padded}'\n"; // 输出: 'PHP*******'
// 实际应用:格式化表格对齐
function createTable($headers, $data) {
// 计算每列的最大宽度
$columnWidths = [];
foreach ($headers as $i => $header) {
$columnWidths[$i] = strlen($header);
foreach ($data as $row) {
$columnWidths[$i] = max($columnWidths[$i], strlen($row[$i]));
}
}
// 格式化表头
$headerRow = "|";
foreach ($headers as $i => $header) {
$headerRow .= " " . str_pad($header, $columnWidths[$i]) . " |";
}
$headerRow .= "\n";
// 格式化分隔线
$separator = "+";
foreach ($columnWidths as $width) {
$separator .= "-" . str_repeat("-", $width + 2) . "-+";
}
$separator .= "\n";
// 格式化数据行
$dataRows = "";
foreach ($data as $row) {
$dataRows .= "|";
foreach ($row as $i => $cell) {
$dataRows .= " " . str_pad($cell, $columnWidths[$i]) . " |";
}
$dataRows .= "\n";
}
return $separator . $headerRow . $separator . $dataRows . $separator;
}
$headers = ["姓名", "年龄", "城市", "职业"];
$data = [
["张三", "28", "北京", "工程师"],
["李四", "25", "上海", "设计师"],
["王五", "32", "广州", "产品经理"]
];
echo createTable($headers, $data);
?>
sprintf() 对齐功能
<?php
// 使用sprintf实现字符串对齐
$data = [
["产品", "数量", "价格"],
["苹果", "100", "¥5.99"],
["香蕉", "50", "¥3.50"],
["橙子", "75", "¥4.25"]
];
foreach ($data as $row) {
printf("%-10s | %6s | %10s\n", ...$row);
}
// 创建动态对齐函数
function alignColumns($data, $padding = 2) {
if (empty($data)) return "";
// 计算每列最大宽度
$columnWidths = [];
$colCount = count($data[0]);
for ($col = 0; $col < $colCount; $col++) {
$maxWidth = 0;
foreach ($data as $row) {
$maxWidth = max($maxWidth, mb_strwidth($row[$col], 'UTF-8'));
}
$columnWidths[] = $maxWidth + $padding;
}
// 格式化输出
$result = "";
foreach ($data as $row) {
$formatted = "";
foreach ($row as $col => $cell) {
$formatted .= str_pad($cell, $columnWidths[$col]);
}
$result .= rtrim($formatted) . "\n";
}
return $result;
}
$tableData = [
["中文名称", "English Name", "价格"],
["苹果", "Apple", "¥5.99"],
["香蕉", "Banana", "¥3.50"],
["橙子", "Orange", "¥4.25"]
];
echo alignColumns($tableData);
?>
日期和时间格式化
date() - 基本日期格式化
<?php
// 常用日期格式
$timestamp = time();
echo "当前时间戳: {$timestamp}\n";
echo "Y-m-d: " . date('Y-m-d', $timestamp) . "\n"; // 2024-01-15
echo "H:i:s: " . date('H:i:s', $timestamp) . "\n"; // 14:30:25
echo "Y-m-d H:i:s: " . date('Y-m-d H:i:s', $timestamp) . "\n"; // 2024-01-15 14:30:25
// 更多格式选项
echo "年份(Y): " . date('Y') . "\n"; // 2024 (4位)
echo "年份(y): " . date('y') . "\n"; // 24 (2位)
echo "月份(F): " . date('F') . "\n"; // January (英文全称)
echo "月份(M): " . date('M') . "\n"; // Jan (英文缩写)
echo "月份(m): " . date('m') . "\n"; // 01 (数字,带前导零)
echo "月份(n): " . date('n') . "\n"; // 1 (数字,不带前导零)
echo "日期(d): " . date('d') . "\n"; // 15 (带前导零)
echo "日期(j): " . date('j') . "\n"; // 15 (不带前导零)
echo "星期(l): " . date('l') . "\n"; // Monday (英文全称)
echo "星期(D): " . date('D') . "\n"; // Mon (英文缩写)
echo "星期(N): " . date('N') . "\n"; // 1 (ISO-8601数字表示,1-7)
// 时间格式
echo "小时(H): " . date('H') . "\n"; // 14 (24小时制,带前导零)
echo "小时(h): " . date('h') . "\n"; // 02 (12小时制,带前导零)
echo "分钟(i): " . date('i') . "\n"; // 30 (带前导零)
echo "秒(s): " . date('s') . "\n"; // 25 (带前导零)
echo "上午下午(a): " . date('a') . "\n"; // am/pm
echo "上午下午(A): " . date('A') . "\n"; // AM/PM
// 实用函数:友好的时间显示
function friendlyTime($timestamp) {
$now = time();
$diff = $now - $timestamp;
if ($diff < 60) {
return "刚刚";
} elseif ($diff < 3600) {
$minutes = floor($diff / 60);
return "{$minutes}分钟前";
} elseif ($diff < 86400) {
$hours = floor($diff / 3600);
return "{$hours}小时前";
} elseif ($diff < 2592000) { // 30天
$days = floor($diff / 86400);
return "{$days}天前";
} else {
return date('Y-m-d', $timestamp);
}
}
$past = time() - 3661; // 1小时1分钟前
echo "友好时间: " . friendlyTime($past) . "\n";
// 本地化日期格式化
function formatDateLocalized($timestamp, $locale = 'zh_CN') {
switch ($locale) {
case 'zh_CN':
$weekdays = ['日', '一', '二', '三', '四', '五', '六'];
$weekday = $weekdays[date('w', $timestamp)];
return date('Y年m月d日 星期' . $weekday, $timestamp);
case 'en_US':
return date('l, F j, Y', $timestamp);
case 'de_DE':
$germanMonths = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
$month = $germanMonths[date('n', $timestamp) - 1];
return date('j. ') . $month . date(' Y', $timestamp);
default:
return date('Y-m-d', $timestamp);
}
}
echo "中文日期: " . formatDateLocalized(time(), 'zh_CN') . "\n";
echo "英文日期: " . formatDateLocalized(time(), 'en_US') . "\n";
echo "德文日期: " . formatDateLocalized(time(), 'de_DE') . "\n";
?>
DateTime 类 - 面向对象的日期处理
<?php
// 创建DateTime对象
$now = new DateTime();
echo "当前时间: " . $now->format('Y-m-d H:i:s') . "\n";
// 从字符串创建DateTime
$date = DateTime::createFromFormat('Y-m-d', '2024-01-15');
echo "指定日期: " . $date->format('Y年m月d日') . "\n";
// 日期修改
$date->modify('+1 week');
echo "一周后: " . $date->format('Y-m-d') . "\n";
$date->modify('+2 days');
echo "再两天: " . $date->format('Y-m-d') . "\n";
// 日期比较
$date1 = new DateTime('2024-01-01');
$date2 = new DateTime('2024-01-15');
if ($date1 < $date2) {
echo "date1 早于 date2\n";
}
$interval = $date1->diff($date2);
echo "相差: " . $interval->days . " 天\n";
// 实用类:日期格式化工具
class DateFormatter {
private $dateTime;
public function __construct($date = null) {
if ($date instanceof DateTime) {
$this->dateTime = $date;
} elseif (is_string($date)) {
$this->dateTime = new DateTime($date);
} elseif (is_int($date)) {
$this->dateTime = DateTime::createFromFormat('U', $date);
} else {
$this->dateTime = new DateTime();
}
}
public function format($format) {
return $this->dateTime->format($format);
}
public function toChinese() {
$weekdays = ['日', '一', '二', '三', '四', '五', '六'];
$weekday = $weekdays[$this->dateTime->format('w')];
return $this->dateTime->format('Y年m月d日 星期') . $weekday;
}
public function toISO() {
return $this->dateTime->format('c');
}
public function toMySQL() {
return $this->dateTime->format('Y-m-d H:i:s');
}
public function relativeTo($otherDate = null) {
if ($otherDate === null) {
$otherDate = new DateTime();
} elseif (!$otherDate instanceof DateTime) {
$otherDate = new DateTime($otherDate);
}
$interval = $this->dateTime->diff($otherDate);
if ($interval->invert) {
$prefix = "之后";
} else {
$prefix = "之前";
}
if ($interval->y > 0) {
return $interval->y . "年" . $prefix;
} elseif ($interval->m > 0) {
return $interval->m . "个月" . $prefix;
} elseif ($interval->d > 0) {
return $interval->d . "天" . $prefix;
} elseif ($interval->h > 0) {
return $interval->h . "小时" . $prefix;
} elseif ($interval->i > 0) {
return $interval->i . "分钟" . $prefix;
} else {
return "刚刚";
}
}
public function addDays($days) {
$this->dateTime->modify("+{$days} days");
return $this;
}
public function addMonths($months) {
$this->dateTime->modify("+{$months} months");
return $this;
}
}
// 使用示例
$formatter = new DateFormatter('2024-01-15');
echo "中文格式: " . $formatter->toChinese() . "\n";
echo "ISO格式: " . $formatter->toISO() . "\n";
echo "MySQL格式: " . $formatter->toMySQL() . "\n";
echo "相对时间: " . $formatter->relativeTo() . "\n";
$formatter->addDays(7);
echo "一周后: " . $formatter->format('Y-m-d') . "\n";
?>
模板和占位符系统
简单模板系统
<?php
class SimpleTemplate {
private $template;
private $variables = [];
public function __construct($template) {
$this->template = $template;
}
public function set($name, $value) {
$this->variables[$name] = $value;
return $this;
}
public function setMultiple($variables) {
$this->variables = array_merge($this->variables, $variables);
return $this;
}
public function render() {
$result = $this->template;
// 替换简单变量 {{variable}}
$result = preg_replace_callback('/\{\{(\w+)\}\}/', function($matches) {
$var = $matches[1];
return isset($this->variables[$var]) ? $this->variables[$var] : $matches[0];
}, $result);
// 替换函数调用 {{func()}}
$result = preg_replace_callback('/\{\{(\w+)\(\)\}\}/', function($matches) {
$func = $matches[1];
if (function_exists($func)) {
return $func();
}
return $matches[0];
}, $result);
return $result;
}
public function renderAndEcho() {
echo $this->render();
}
}
// 使用示例
$template = new SimpleTemplate(<<<TEMPLATE
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{greeting}},{{name}}!</h1>
<p>当前时间:{{date()}}</p>
<table border="1">
<tr><th>产品</th><th>价格</th></tr>
{{product_rows}}
</table>
</body>
</html>
TEMPLATE);
$template->setMultiple([
'title' => '欢迎页面',
'greeting' => '欢迎',
'name' => '张三'
]);
// 生成产品行
$products = [
['name' => '苹果', 'price' => '¥5.99'],
['name' => '香蕉', 'price' => '¥3.50'],
['name' => '橙子', 'price' => '¥4.25']
];
$productRows = "";
foreach ($products as $product) {
$productRows .= "<tr><td>{$product['name']}</td><td>{$product['price']}</td></tr>";
}
$template->set('product_rows', $productRows);
$template->renderAndEcho();
?>
高级模板引擎
<?php
class AdvancedTemplate {
private $template;
private $variables = [];
private $functions = [];
public function __construct($template) {
$this->template = $template;
$this->functions = [
'upper' => 'strtoupper',
'lower' => 'strtolower',
'date' => function($format = 'Y-m-d') { return date($format); },
'currency' => function($amount, $symbol = '¥') {
return $symbol . number_format($amount, 2);
},
'truncate' => function($text, $length = 50) {
return strlen($text) > $length ? substr($text, 0, $length) . '...' : $text;
}
];
}
public function set($name, $value) {
$this->variables[$name] = $value;
return $this;
}
public function setFunction($name, callable $func) {
$this->functions[$name] = $func;
return $this;
}
public function render() {
$result = $this->template;
// 处理条件语句 {% if condition %} ... {% endif %}
$result = $this->processConditions($result);
// 处理循环语句 {% for item in items %} ... {% endfor %}
$result = $this->processLoops($result);
// 处理变量和函数调用
$result = $this->processVariables($result);
return $result;
}
private function processConditions($template) {
$pattern = '/\{%\s*if\s+(\w+)\s*%\}(.*?)\{%\s*endif\s*%\}/s';
return preg_replace_callback($pattern, function($matches) {
$condition = $matches[1];
$content = $matches[2];
$value = isset($this->variables[$condition]) ? $this->variables[$condition] : null;
return $value ? $content : '';
}, $template);
}
private function processLoops($template) {
$pattern = '/\{%\s*for\s+(\w+)\s+in\s+(\w+)\s*%\}(.*?)\{%\s*endfor\s*%\}/s';
return preg_replace_callback($pattern, function($matches) {
$itemVar = $matches[1];
$arrayVar = $matches[2];
$content = $matches[3];
$array = isset($this->variables[$arrayVar]) ? $this->variables[$arrayVar] : [];
$result = '';
foreach ($array as $item) {
$tempContent = $content;
// 替换循环变量
if (is_array($item)) {
foreach ($item as $key => $value) {
$tempContent = str_replace('{{' . $itemVar . '.' . $key . '}}', $value, $tempContent);
}
} else {
$tempContent = str_replace('{{' . $itemVar . '}}', $item, $tempContent);
}
$result .= $tempContent;
}
return $result;
}, $template);
}
private function processVariables($template) {
// 处理函数调用 {{func(arg1, arg2)}}
$template = preg_replace_callback('/\{\{(\w+)\(([^)]*)\)\}\}/', function($matches) {
$funcName = $matches[1];
$argsStr = $matches[2];
if (!isset($this->functions[$funcName])) {
return $matches[0];
}
// 解析参数
$args = [];
if (!empty($argsStr)) {
$argList = array_map('trim', explode(',', $argsStr));
foreach ($argList as $arg) {
// 如果参数是变量引用
if (preg_match('/^\$?\w+$/', $arg)) {
$varName = ltrim($arg, '$');
$args[] = isset($this->variables[$varName]) ? $this->variables[$varName] : '';
} elseif (preg_match('/^["\'].*["\']$/', $arg)) {
// 字符串参数
$args[] = trim($arg, '"\'');
} else {
// 数字参数
$args[] = is_numeric($arg) ? $arg + 0 : $arg;
}
}
}
return call_user_func_array($this->functions[$funcName], $args);
}, $template);
// 处理简单变量 {{variable}}
$template = preg_replace_callback('/\{\{(\w+(?:\.\w+)*)\}\}/', function($matches) {
$varPath = $matches[1];
$parts = explode('.', $varPath);
$value = $this->variables;
foreach ($parts as $part) {
if (is_array($value) && isset($value[$part])) {
$value = $value[$part];
} else {
return $matches[0];
}
}
return $value;
}, $template);
return $template;
}
}
// 使用示例
$template = new AdvancedTemplate(<<<TEMPLATE
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{greeting}},{{user.name}}!</h1>
{% if show_message %}
<p>{{user.message | truncate(100) | upper}}</p>
{% endif %}
<h2>订单详情</h2>
<p>订单时间:{{date('Y-m-d H:i:s')}}</p>
<table border="1">
<tr>
<th>产品名称</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
{% for item in order.items %}
<tr>
<td>{{item.name}}</td>
<td>{{item.quantity}}</td>
<td>{{currency(item.price)}}</td>
<td>{{currency(item.quantity * item.price)}}</td>
</tr>
{% endfor %}
<tr>
<td colspan="3"><strong>总计</strong></td>
<td><strong>{{currency(order.total)}}</strong></td>
</tr>
</table>
<p>感谢您的惠顾!</p>
</body>
</html>
TEMPLATE);
// 设置模板变量
$template->set('title', '订单确认');
$template->set('greeting', '尊敬的客户');
$template->set('show_message', true);
$template->set('user', [
'name' => '李四',
'message' => '感谢您购买我们的产品,这是您的订单确认信息。希望您对本次购物体验满意,期待您的再次光临!'
]);
$template->set('order', [
'items' => [
['name' => '笔记本电脑', 'quantity' => 1, 'price' => 4999.00],
['name' => '无线鼠标', 'quantity' => 2, 'price' => 99.00],
['name' => 'USB键盘', 'quantity' => 1, 'price' => 159.00]
],
'total' => 5356.00
]);
// 渲染模板
echo $template->render();
?>
实际应用示例
CSV文件生成器
<?php
class CSVGenerator {
private $data;
private $headers;
private $filename;
public function __construct($data, $headers = [], $filename = 'export.csv') {
$this->data = $data;
$this->headers = $headers;
$this->filename = $filename;
}
public function generate() {
$csv = '';
// 添加BOM以支持中文
$csv = "\xEF\xBB\xBF";
// 添加表头
if (!empty($this->headers)) {
$csv .= $this->formatRow($this->headers);
}
// 添加数据行
foreach ($this->data as $row) {
$csv .= $this->formatRow($row);
}
return $csv;
}
private function formatRow($row) {
$formatted = [];
foreach ($row as $cell) {
// 处理包含逗号、引号或换行符的字段
if (is_string($cell) && (strpos($cell, ',') !== false ||
strpos($cell, '"') !== false ||
strpos($cell, "\n") !== false)) {
$cell = '"' . str_replace('"', '""', $cell) . '"';
}
$formatted[] = $cell;
}
return implode(',', $formatted) . "\n";
}
public function download() {
$csv = $this->generate();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $this->filename);
header('Content-Length: ' . strlen($csv));
echo $csv;
exit;
}
public function save($filepath) {
$csv = $this->generate();
return file_put_contents($filepath, $csv) !== false;
}
}
// 使用示例
$users = [
['name' => '张三', 'email' => 'zhangsan@example.com', 'age' => 28, 'city' => '北京'],
['name' => '李四', 'email' => 'lisi@example.org', 'age' => 25, 'city' => '上海'],
['name' => '王五', 'email' => 'wangwu@example.net', 'age' => 32, 'city' => '广州']
];
$headers = ['姓名', '邮箱', '年龄', '城市'];
$csvGenerator = new CSVGenerator($users, $headers, 'users.csv');
// 生成CSV内容
$csvContent = $csvGenerator->generate();
echo $csvContent;
// 或者直接下载
// $csvGenerator->download();
// 或者保存到文件
// $csvGenerator->save('path/to/users.csv');
?>
报表生成器
<?php
class ReportGenerator {
private $data;
private $title;
private $columns;
public function __construct($title, $data, $columns) {
$this->title = $title;
$this->data = $data;
$this->columns = $columns;
}
public function generateText() {
$report = $this->title . "\n";
$report .= str_repeat('=', mb_strlen($this->title, 'UTF-8')) . "\n\n";
// 计算列宽
$widths = [];
foreach ($this->columns as $key => $config) {
$widths[$key] = mb_strlen($config['title'], 'UTF-8');
foreach ($this->data as $row) {
$value = $this->formatValue($row[$key] ?? '', $config['format'] ?? 'text');
$widths[$key] = max($widths[$key], mb_strlen($value, 'UTF-8'));
}
}
// 表头
$header = '|';
$separator = '+';
foreach ($this->columns as $key => $config) {
$width = $widths[$key];
$header .= ' ' . str_pad($config['title'], $width) . ' |';
$separator .= '-' . str_repeat('-', $width + 2) . '-+';
}
$report .= $separator . "\n" . $header . "\n" . $separator . "\n";
// 数据行
foreach ($this->data as $row) {
$line = '|';
foreach ($this->columns as $key => $config) {
$value = $this->formatValue($row[$key] ?? '', $config['format'] ?? 'text');
$align = $config['align'] ?? 'left';
if ($align === 'right') {
$line .= ' ' . str_pad($value, $widths[$key], ' ', STR_PAD_LEFT) . ' |';
} else {
$line .= ' ' . str_pad($value, $widths[$key]) . ' |';
}
}
$report .= $line . "\n";
}
$report .= $separator . "\n";
// 添加统计信息
$report .= "\n统计信息:\n";
$report .= "总记录数: " . count($this->data) . "\n";
$report .= "生成时间: " . date('Y-m-d H:i:s') . "\n";
return $report;
}
public function generateHTML() {
$html = '<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>' . htmlspecialchars($this->title) . '</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; font-weight: bold; }
tr:nth-child(even) { background-color: #f9f9f9; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.summary { background-color: #f0f0f0; padding: 10px; border-radius: 5px; }
</style>
</head>
<body>
<h1>' . htmlspecialchars($this->title) . '</h1>
<table>
<thead>
<tr>';
foreach ($this->columns as $config) {
$align = $config['align'] ?? 'left';
$class = $align === 'right' ? 'text-right' : ($align === 'center' ? 'text-center' : '');
$html .= '<th class="' . $class . '">' . htmlspecialchars($config['title']) . '</th>';
}
$html .= '</tr>
</thead>
<tbody>';
foreach ($this->data as $row) {
$html .= '<tr>';
foreach ($this->columns as $key => $config) {
$value = $this->formatValue($row[$key] ?? '', $config['format'] ?? 'text');
$align = $config['align'] ?? 'left';
$class = $align === 'right' ? 'text-right' : ($align === 'center' ? 'text-center' : '');
$html .= '<td class="' . $class . '">' . htmlspecialchars($value) . '</td>';
}
$html .= '</tr>';
}
$html .= '</tbody>
</table>
<div class="summary">
<p><strong>统计信息:</strong></p>
<p>总记录数: ' . count($this->data) . '</p>
<p>生成时间: ' . date('Y-m-d H:i:s') . '</p>
</div>
</body>
</html>';
return $html;
}
private function formatValue($value, $format) {
switch ($format) {
case 'currency':
return '¥' . number_format($value, 2);
case 'number':
return number_format($value);
case 'percentage':
return number_format($value * 100, 2) . '%';
case 'date':
if (is_numeric($value)) {
return date('Y-m-d', $value);
}
return $value;
case 'datetime':
if (is_numeric($value)) {
return date('Y-m-d H:i:s', $value);
}
return $value;
default:
return (string) $value;
}
}
}
// 使用示例
$salesData = [
['product' => '笔记本电脑', 'sales' => 120, 'revenue' => 599880, 'date' => '2024-01-01'],
['product' => '无线鼠标', 'sales' => 450, 'revenue' => 44550, 'date' => '2024-01-02'],
['product' => 'USB键盘', 'sales' => 280, 'revenue' => 44520, 'date' => '2024-01-03'],
['product' => '显示器', 'sales' => 95, 'revenue' => 142500, 'date' => '2024-01-04'],
['product' => '耳机', 'sales' => 320, 'revenue' => 64000, 'date' => '2024-01-05']
];
$columns = [
'product' => ['title' => '产品名称', 'format' => 'text', 'align' => 'left'],
'sales' => ['title' => '销售数量', 'format' => 'number', 'align' => 'right'],
'revenue' => ['title' => '销售收入', 'format' => 'currency', 'align' => 'right'],
'date' => ['title' => '日期', 'format' => 'date', 'align' => 'center']
];
$report = new ReportGenerator('销售报表', $salesData, $columns);
echo "文本格式报表:\n";
echo $report->generateText() . "\n\n";
echo "HTML格式报表:\n";
echo $report->generateHTML();
?>
邮件模板系统
<?php
class EmailTemplate {
private $template;
private $variables;
private $layout;
public function __construct($template, $layout = null) {
$this->template = $template;
$this->variables = [];
$this->layout = $layout;
}
public function set($name, $value) {
$this->variables[$name] = $value;
return $this;
}
public function setMultiple($variables) {
$this->variables = array_merge($this->variables, $variables);
return $this;
}
public function render() {
$content = $this->template;
// 处理条件渲染 {% if variable %}...{% endif %}
$content = preg_replace_callback(
'/{%\s*if\s+(\w+)\s*%}(.*?)(?:{%\s*else\s*%}(.*?))?{%\s*endif\s*%}/s',
function($matches) {
$condition = $matches[1];
$ifContent = $matches[2] ?? '';
$elseContent = $matches[3] ?? '';
return !empty($this->variables[$condition]) ? $ifContent : $elseContent;
},
$content
);
// 处理变量替换 {{variable}}
$content = preg_replace_callback(
'/\{\{(\w+(?:\.\w+)*)\}\}/',
function($matches) {
$path = $matches[1];
$keys = explode('.', $path);
$value = $this->variables;
foreach ($keys as $key) {
if (is_array($value) && isset($value[$key])) {
$value = $value[$key];
} else {
return $matches[0];
}
}
return $value;
},
$content
);
// 处理格式化函数 {{format:function(args)}}
$content = preg_replace_callback(
'/\{\{format:(\w+)\((.*?)\)\}\}/',
function($matches) {
$funcName = $matches[1];
$argsStr = $matches[2];
$args = [];
if (!empty($argsStr)) {
$argList = array_map('trim', explode(',', $argsStr));
foreach ($argList as $arg) {
if (preg_match('/^\$?\w+$/', $arg)) {
$varName = ltrim($arg, '$');
$args[] = $this->variables[$varName] ?? '';
} else {
$args[] = trim($arg, '"\'');
}
}
}
switch ($funcName) {
case 'currency':
return '¥' . number_format($args[0] ?? 0, 2);
case 'date':
return date($args[0] ?? 'Y-m-d', strtotime($args[1] ?? 'now'));
case 'upper':
return strtoupper($args[0] ?? '');
case 'lower':
return strtolower($args[0] ?? '');
default:
return $matches[0];
}
},
$content
);
// 应用布局
if ($this->layout) {
$this->variables['content'] = $content;
$layoutTemplate = new EmailTemplate($this->layout);
$layoutTemplate->variables = $this->variables;
return $layoutTemplate->render();
}
return $content;
}
public function send($to, $subject) {
$headers = [
'MIME-Version: 1.0',
'Content-Type: text/html; charset=UTF-8',
'From: noreply@example.com'
];
$content = $this->render();
return mail($to, $subject, $content, implode("\r\n", $headers));
}
}
// 邮件模板示例
$orderConfirmationTemplate = <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>订单确认</title>
</head>
<body style="font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5;">
<div style="max-width: 600px; margin: 0 auto; background-color: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<h1 style="color: #333; text-align: center;">订单确认</h1>
<p>尊敬的 {{customer.name}}:</p>
<p>感谢您的订单!我们已经收到您的订单,正在为您准备商品。</p>
<div style="margin: 20px 0;">
<h3 style="color: #555; border-bottom: 2px solid #eee; padding-bottom: 10px;">订单信息</h3>
<p><strong>订单号:</strong>{{order.number}}</p>
<p><strong>下单时间:</strong>{{format:date('Y年m月d日 H:i', order.created_at)}}</p>
<p><strong>支付方式:</strong>{{order.payment_method}}</p>
</div>
<div style="margin: 20px 0;">
<h3 style="color: #555; border-bottom: 2px solid #eee; padding-bottom: 10px;">配送地址</h3>
<p>{{customer.address}}</p>
<p>{{customer.city}} {{customer.province}} {{customer.postal_code}}</p>
<p>电话:{{customer.phone}}</p>
</div>
<div style="margin: 20px 0;">
<h3 style="color: #555; border-bottom: 2px solid #eee; padding-bottom: 10px;">订单明细</h3>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f8f8f8;">
<th style="padding: 10px; text-align: left; border: 1px solid #ddd;">商品</th>
<th style="padding: 10px; text-align: center; border: 1px solid #ddd;">数量</th>
<th style="padding: 10px; text-align: right; border: 1px solid #ddd;">单价</th>
<th style="padding: 10px; text-align: right; border: 1px solid #ddd;">小计</th>
</tr>
</thead>
<tbody>
{% for item in order.items %}
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">{{item.name}}</td>
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">{{item.quantity}}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #ddd;">{{format:currency(item.price)}}</td>
<td style="padding: 10px; text-align: right; border: 1px solid #ddd;">{{format:currency(item.quantity * item.price)}}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" style="padding: 10px; text-align: right; border: 1px solid #ddd; font-weight: bold;">总计</td>
<td style="padding: 10px; text-align: right; border: 1px solid #ddd; font-weight: bold;">{{format:currency(order.total)}}</td>
</tr>
</tfoot>
</table>
</div>
{% if order.note %}
<div style="margin: 20px 0;">
<h3 style="color: #555; border-bottom: 2px solid #eee; padding-bottom: 10px;">订单备注</h3>
<p>{{order.note}}</p>
</div>
{% endif %}
<div style="margin: 30px 0; padding: 20px; background-color: #f0f8ff; border-radius: 5px;">
<p style="margin: 0; text-align: center;">如有任何问题,请联系我们的客服团队</p>
<p style="margin: 5px 0; text-align: center;">电话:400-123-4567 | 邮箱:service@example.com</p>
</div>
<p style="text-align: center; color: #666; font-size: 12px; margin-top: 30px;">
此邮件由系统自动发送,请勿回复
</p>
</div>
</body>
</html>
HTML;
// 使用示例
$email = new EmailTemplate($orderConfirmationTemplate);
$email->setMultiple([
'customer' => [
'name' => '李明',
'address' => '中关村大街1号',
'city' => '北京市',
'province' => '北京',
'postal_code' => '100080',
'phone' => '13812345678'
],
'order' => [
'number' => 'ORD202401150001',
'created_at' => '2024-01-15 10:30:00',
'payment_method' => '支付宝',
'total' => 5356.00,
'note' => '请在工作日配送',
'items' => [
['name' => '笔记本电脑', 'quantity' => 1, 'price' => 4999.00],
['name' => '无线鼠标', 'quantity' => 2, 'price' => 99.00],
['name' => 'USB键盘', 'quantity' => 1, 'price' => 159.00]
]
]
]);
echo $email->render();
// 发送邮件(需要配置邮件服务器)
// $email->send('customer@example.com', '订单确认 - ORD202401150001');
?>
本节练习
基础练习
- 使用
number_format()格式化不同类型的数字(价格、百分比、科学计数法等) - 使用
printf()和sprintf()创建格式化的表格输出 - 使用
str_pad()实现对齐的文本显示 - 使用
date()函数创建各种日期格式
进阶练习
- 创建一个完整的报表生成器,支持多种输出格式
- 实现一个简单的模板引擎,支持变量替换和条件渲染
- 开发一个CSV导入导出工具
- 创建一个邮件模板系统,支持动态内容生成
实战练习
- 完善AdvancedTemplate类,添加更多功能(循环、过滤器等)
- 扩展EmailTemplate类,支持邮件队列和批量发送
- 创建一个数据可视化工具,将数据转换为图表格式
- 实现一个多语言支持的格式化系统
总结
本节我们学习了PHP字符串格式化的各种技术:
- 数字格式化:
number_format(),printf(),sprintf()等 - 字符串填充对齐:
str_pad(),sprintf()对齐功能 - 日期时间格式化:
date()函数和DateTime类 - 模板系统:从简单模板到高级模板引擎
- 实际应用:CSV生成、报表生成、邮件系统等
字符串格式化是Web开发中的重要技能,它能让数据显示更加美观和专业。掌握这些技术将帮助你创建用户友好的界面和专业的报告系统。
💡 学习建议:
- 熟悉各种格式化函数的参数和用法
- 注意本地化和国际化问题
- 在实际项目中灵活应用不同的格式化技术
- 考虑性能优化,避免过度格式化
- 始终考虑安全性,特别是在处理用户输入时
章节总结:通过本章的学习,你已经掌握了PHP字符串处理的全面知识,从基础操作到高级应用,为后续的Web开发打下了坚实的基础。