今天让我们来看一个插件开发的反例 - 显示在线用户
Tillreetree 9小时前

原始代码:

<div class="container">
<div class="row">
	<div class="col-lg-12">
		<div class="card card-friendlink link-bottom">
			<div class="card-header">在线用户--<?php
			$arronline=assoc_unique(online_list_cache(),'uid');//获取在线用户信息并且根据uid值去重复
			echo count($arronline);
			?></div>
			<div class="card-body p-a-sm p-b-xs">
					<?php 
$rew=$conf['url_rewrite_on']==0 ? '?':'';//伪静态
foreach($arronline as $online_user) { //Changed By Eveson 
echo "<a class=\"mr-2 float-left\" href=\"".getlistn().$rew."user-{$online_user['uid']}.htm\" target=\"_blank\" >{$online_user['username']}</a>";//此处感觉应该引入js或者html文件
}
function assoc_unique($arr, $key) {  //根据指定的key值为数组去重
        $tmp_arr = array();  
        foreach($arr as $k => $v) {  
            if(in_array($v[$key], $tmp_arr)) {  
                unset($arr[$k]);  
            } else {  
                $tmp_arr[] = $v[$key];  
            }  
        }  
        sort($arr);  
        return $arr;  
} 
function getlistn(){//返回修罗主程序所处相对目录 
return substr($_SERVER['PHP_SELF'],0,strlen($_SERVER['PHP_SELF'])-10);   
}
                    
                    ?>
			</div>
		</div>
	</div>
	</div>

这是一个典型的“面条式”代码示例,虽然功能上可以实现,但从插件开发的角度来看,它存在多个严重的问题,是我们在开发中应该避免的反面教材。

问题在哪:

  1. 原始代码将assoc_unique和getlistn函数定义在HTML输出逻辑之后,导致PHP解析时可能会先调用未声明的函数,引发致命错误(Fatal error: Call to undefined function)

  2. 路径计算逻辑脆弱:getlistn()通过硬编码截取$_SERVER['PHP_SELF']末尾10字符(大约为“/index.php”的长度,算上符号10个字符),实际路径长度可能变化(如子目录部署),导致生成错误URL,实际上整个xiuno只需要相对路径即可

  3. 手动拼接$rew变量判断伪静态,不如使用统一的URL生成函数

  4. 代码结构混乱:PHP函数定义与HTML模板混杂,降低可维护性;xiuno bbs推荐你直接用PHP作为模板引擎,但不是说你可以这样过度混乱的去写

  5. 重复定义风险:原始代码未检查函数是否存在(我们不能做假设性的判断)

  6. 过时语法:使用已废弃的float-left等旧版Bootstrap类

修改版:

<?php 

if(!function_exists('assoc_unique')){
	function assoc_unique($arr, $key) {  //根据指定的key值为数组去重
        $tmp_arr = array();  
        foreach($arr as $k => $v) {  
            if(in_array($v[$key], $tmp_arr)) {  
                unset($arr[$k]);  
            } else {  
                $tmp_arr[] = $v[$key];  
            }  
        }  
        sort($arr);  
        return $arr;  
	} 
}

//获取在线用户信息并且根据uid值去重复
$arronline=assoc_unique(online_list_cache(),'uid');
?>

<div class="row">
	<div class="col-lg-12">
		<div class="card card-friendlink link-bottom">
			<div class="card-header">在线用户 (<?php echo count($arronline); ?>) </div>
			<div class="card-body" hx-boost="true" hx-target="#body" hx-swap="innerHTML">
				<?php foreach($arronline as $online_user) : ?>
                                <a class="btn btn-link" href="<?= url('user-' . $online_user['uid']) ?>"><?= htmlspecialchars($online_user['username'], ENT_QUOTES, 'UTF-8') ?></a>
				<?php endforeach; ?>
			</div>
		</div>
	</div>
</div>

修改版的优化思路:

  1. 将PHP数据处理逻辑完全移至HTML模板之前,结构清晰,一目了然。

  2. 使用 if(!function_exists(...)) 包装函数定义,有效避免了函数重定义冲突。

  3. 使用安全的URL生成函数:使用xiuno bbs唯一指定URL生成函数 url() ,它能智能地根据当前配置(如是否开启伪静态等)生成正确的URL,彻底解决了手动拼接的隐患。这是与主程序交互的正确方式。

  4. 使用防御性编程:虽然我们从来都没在用户名上吃瘪,且所有人都会输入正常的用户名,但以防万一
  5. 使用更优雅的模板语法:采用 foreach(...) : ... endforeach; 替代大括号{},在HTML环境中更清晰。使用短标签 <?= 让输出语句更简洁。

    1. 以及,短标签 <?= ... ?> 是PHP一直可用的方式,不需要开启short_open_tag即可使用
  6. 重新改进HTML结构与样式:使用 btn-link 来给链接增加边距,并且不再需要任何float就可以正确排版,视觉效果更好。移除了不必要的内联样式。

  7. 添加了HTMX属性,这不是必须的,但这个例子是来自于我正在开发的HTMX现代化计划的一部分,所以就留着了

通过这样的重构,代码从一段“能用但危险”的脚本转变为了一个结构清晰、健壮可靠且易于维护的插件组件。

最新回复 (2)
全部楼主
  • Tillreetree 版主 楼主
    9小时前 2
    0

    原始代码的“笨拙”三宗罪:

    1. “手工耿”式的字符串拼接:
      "<a class=\"mr-2 float-left\" href=\"".getlistn().$rew."user-{$online_user['uid']}.htm\" target=\"_blank\" >{$online_user['username']}</a>"
      这行代码像用铁丝和胶带组装一件精密仪器。大量的引号转义、连接符 . 和变量插值 {} 混杂在一起,可读性极差,非常容易出错(比如少个点、多个引号)。修改版使用清晰的 <?= ?> 短标签在 HTML 中直接嵌入变量,结构一目了然。

    2. 重新发明了一个有 bug 的轮子(URL 生成):
      作者自己造了一个 getlistn() 函数来计算基础路径,又手动判断 $rew 来拼接问号。这是一个典型的过度工程且不可靠的方案。

      • 它假设所有部署环境都是固定的,无法处理子目录等复杂情况。

      • 它硬编码了 .htm 后缀,与主程序的路由规则形成了强耦合。一旦主程序路由变化,所有这样的链接全部失效。

      • 正如您所说,url() 函数就是专门为解决这个问题而生的“瑞士军刀”,它抽象了所有复杂的路由逻辑,开发者只需关心目的('user-123'),而无需关心实现(怎么生成URL)。拒绝使用框架提供的工具,就是在给自己和项目埋坑。

    3. 将表现与逻辑以最坏的方式混合:
      不仅在 HTML 中嵌入了 PHP,还在输出语句中嵌套了函数调用和复杂逻辑。这违反了关注点分离的原则,使得代码难以调试和维护。

  • qscm521
    5小时前 3
    0
    楼主,我只是来混个熟的!老板,侬亿雷凑闹嫩了!
返回