重构老项目惊现神级display函数,你知道多坑吗?!

最近在重构一个老项目,发现前任写了个神一样的display函数。这东西看起来简单,用起来坑多得像蜂窝煤。今天就来聊聊这个让人又爱又恨的display。

先说个真实案例。上周五晚上11点,我正在撸串,突然收到报警短信说网站500了。查了半天发现是某个display函数把数组当字符串输出了,直接把内存撑爆。这种低级错误都能犯,气得我把手里的羊肉串都捏碎了。

最基本的display用法是这样的:

<?php

function display($content) {

echo $content;

}

?>

看起来人畜无害是?但问题就出在这个echo上。你知道echo和print的区别吗?print是个函数,echo是个语言结构。这意味着print有返回值,而echo可以同时输出多个参数。比如:

<?php

echo 'hello', 'world'; // 合法

print 'hello', 'world'; // 报错

在display函数里用echo而不是print,这是有讲究的。因为echo的性能比print高那么一丢丢,在循环里用print的话,每次调用都要创建返回值,浪费资源。

但display真正坑爹的地方在于类型处理。看看这个例子:

<?php

display(['name' => '张三', 'age' => 25]);

?>

你猜会发生什么?直接给你报个Warning说数组不能转换成字符串。正确的做法应该是:

if(is_array($content)) {

print_r($content);

} else {

echo $content;

}

}

?>

你以为这就完了?太天真。print_r输出的数组格式丑得像被门夹过一样。于是我又加了个美化选项:

<?php

function display($content, $pretty = false) {

echo $pretty ? '<pre>' . print_r($content, true) . '</pre>' : print_r($content, true);

}

}

?>

这时候产品经理跑过来说:"这个显示不够直观,能不能加个表格形式?"我内心一万只草泥马奔腾而过,但还是写了个增强版:

<?php

function display($content, $format = 'raw') {

switch($format) {

case 'table':

if(!is_array($content)) {

trigger_error('表格格式需要数组数据', E_USER_WARNING);

return;

}

echo '<table border="1">';

foreach($content as $key => $value) {

echo "<tr><td>$key</td><td>$value</td></tr>";

echo '</table>';

break;

case 'json':

echo json_encode($content, JSON_PRETTY_PRINT);

default:

if(is_array($content)) {

echo '<pre>' . print_r($content, true) . '</pre>';

} else {

echo $content;

}

}

?>

你以为这就完美了?Too young too simple。有天我发现这个函数在API接口里被调用了,直接把HTML标签输出到了JSON响应里。于是又加了个环境检测:

<?php

function display($content, $format = null) {

if($format === null) {

$format = php_sapi_name() === 'cli' ? 'raw' : 'html';

}

// 剩下的逻辑...

}

?>

最坑的是性能问题。有次我用display输出一个10万条记录的查询结果,页面直接卡死了。后来发现print_r处理大数据时性能极差,于是改成了分批输出:

<?php

function displayLargeArray($array, $chunkSize = 1000) {

echo '[';

$chunks = array_chunk($array, $chunkSize);

foreach($chunks as $i => $chunk) {

if($i > 0) echo ',';

echo json_encode($chunk, JSON_PARTIAL_OUTPUT_ON_ERROR);

flush();

}

echo ']';

}

?>

说到flush,这里有个坑。有些PHP配置下flush不生效,需要同时调用ob_flush:

<?php

while (@ob_end_flush());

flush();

安全性也是个老大难问题。直接echo用户输入就是XSS的温床。所以display函数必须要有转义选项:

function display($content, $options = []) {

$defaults = [

'escape' => true,

'format' => null,

'chunkSize' => 1000

];

$options = array_merge($defaults, $options);

if($options['escape'] && is_string($content)) {

$content = htmlspecialchars($content, ENT_QUOTES);

}

?>

你以为这就完了?还没。有次我用display输出一个包含二进制数据的字符串,结果页面乱码了。于是又加了编码处理:

<?php

if(!mb_check_encoding($content, 'UTF-8')) {

$content = mb_convert_encoding($content, 'UTF-8');

}

?>

最搞笑的是,有天我发现这个display函数被调用了2000多次,每次调用都输出一个3KB的CSS文件。查了半天发现是某个菜鸟在循环里调用了display。于是又加了个缓存机制:

<?php

function display($content, $cacheKey = null) {

static $cache = [];

if($cacheKey !== null) {

if(isset($cache[$cacheKey])) {

return;

}

$cache[$cacheKey] = true;

}

?>

现在这个display函数已经膨胀到300多行代码了,比某些框架的渲染引擎还复杂。每次看到它我都想重写,但想想那些依赖它的老代码,还是算了。

最后说个真事:上周面试一个5年经验的PHPer,我问他知道display函数要注意什么,他张口就来"不就是echo吗"。我微笑着把他简历放进了碎纸机。

所以,别小看这个简单的display函数,里面的坑比你前女友的心思还多。记住:在PHP里,越是看起来简单的东西,用起来越要小心。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值