PHP 8.5 中的管道操作符(|>) 是一个强大的新增功能,它允许采用更函数式的编程风格,通过清晰简洁的方式链式连接操作。它将左侧表达式的结果作为右侧函数或方法的第一个参数传递。
$value = "hello world";
$result = $value
|> function3(...)
|> function2(...)
|> function1(...);
在这篇文章中,分享一些在 PHP 代码中使用管道操作符的真实世界示例,以使代码更干净、更易读。
“用户名”输入通常需要规范化。管道使这些步骤易读。
$raw = " ShaoBo Wan ";
$username = $raw
|> trim(...)
|> strtolower(...)
|> (fn($x) => preg_replace('/\s+/', '-', $x));
// Output: "john-doe"
正如你所见,这里每个阶段都是一个专注、可测试的转换:去除前后空格 → 转换为小写 → 空白转为连字符。
在一条流程中清理、验证并将行映射到 DTO。
$rows = [
['name' => ' Widget A ', 'price' => '12.99', 'sku' => 'W-A'],
['name' => 'Widget B', 'price' => 'n/a', 'sku' => 'W-B'], // invalid price
['name' => 'widget c', 'price' => '7.5', 'sku' => 'W-C'],
];
// normalizeRow 可能会去除前后空格,将价格转换为浮点数,统一大小写:
// ['name' => 'Widget A', 'price' => 12.99, 'sku' => 'W-A']
// ['name' => 'Widget B', 'price' => null, 'sku' => 'W-B']
// ['name' => 'Widget C', 'price' => 7.5, 'sku' => 'W-C']
// isValidRow 如果价格为 null 或 <= 0 则返回 false
$products = $rows
|> (fn($xs) => array_map('normalizeRow', $xs))
|> (fn($xs) => array_filter($xs, 'isValidRow'))
|> (fn($xs) => array_map(Product::fromArray(...), $xs));
// Output: [Product('Widget A', 12.99, 'W-A'), Product('Widget C', 7.5, 'W-C')]
这个链式表达了一个管道:规范化、过滤,然后构建。
组合类似中间件的转换来生成最终响应体。
$data = ['msg' => 'hello', 'n' => 3];
$body = $data
|> json_encode(...)
|> (fn($x) => gzencode($x, 6))
|> base64_encode(...);
// Output: H4sIAAAAAAAAA2WOuwrCMBBE…
每个可调用函数都是一个单参数步骤,导致紧凑的线性构建。
在传递给搜索引擎之前,对查询进行清理和分词。
$query = " Hello, WORLD! ";
$tokens = $query
|> trim(...)
|> strtolower(...)
|> (fn($x) => preg_replace('/[^\w\s]/', '', $x))
|> (fn($x) => preg_split('/\s+/', $x))
|> (fn($xs) => array_filter($xs, fn($t) => strlen($t) > 1));
// Output: ['hello', 'world']
这个管道将杂乱的输入转换为准备好用于搜索的索引令牌。
从带有折扣和税费的商品中推导总额,保持无点的风格。
// 假设有辅助函数:
// priceAfterDiscount(['price' => 100, 'discount' => 0.10]) -> 90.0
// applyTax(90.0) 带 10% 税费 -> 99.0
$cartItems = [
['price' => 100, 'discount' => 0.10],
];
$total = $cartItems
|> (fn($xs) => array_map('priceAfterDiscount', $xs))
|> array_sum(...)
|> (fn($sum) => applyTax($sum));
// applyTax 接受一个参数
// Output: 99.0
步骤很清晰:每个商品折扣 → 求和 → 税费;无需临时变量。
这个管道使用时间戳、请求 ID 和 IP 来丰富事件,然后在记录之前删除敏感字段。
$incomingEvent = [
'user' => 'alice',
'email' => 'alice@example.com'
];
$event = $incomingEvent
|> (fn($x) => array_merge($x, ['received_at' => 1696195560])) // 例如 time()
|> (fn($x) => array_merge($x, ['request_id' => '9f2a1c0b7e3d4a56'])) // 示例随机 ID
|> (fn($x) => array_merge($x, ['ip' => '203.0.113.42'])) // 示例 IP
|> (fn($x) => redactSensitive($x)); // 例如屏蔽电子邮件
// Output:
/*
[
'user' => 'alice',
'email' => 'a***@example.com',
'received_at' => 1696195560,
'request_id' => '9f2a1c0b7e3d4a56',
'ip' => '203.0.113.42'
]
*/
在某些图像处理任务中,你可能希望加载图像、调整大小、应用水印,然后优化它以用于 Web 交付。管道操作符可以帮助清晰地表达这个操作序列。
$imagePath = '/images/source/beach.jpg';
$thumb = $imagePath
// 从 beach.jpg 获取 GD 图像资源
|> (fn($p) => imagecreatefromjpeg($p))
// 调整大小为 320×240
|> (fn($im) => resizeImage($im, 320, 240))
// 添加水印(例如,右下角)
|> (fn($im) => applyWatermark($im))
// 返回二进制 JPEG 数据或保存的文件路径
|> (fn($im) => optimizeJpeg($im, 75));
// Output: '/images/thumbs/beach_320x240_q75.jpg'
每个步骤都是一个单参数转换,返回下一个值,从而生成一个优化的缩略图。