我早期做过一个 WordPress-function-modular 项目,用来把常用函数按模块开关。它记录了当时解决 functions.php 膨胀问题的思路,但仓库多年未维护,不应直接当作现代生产插件安装。
旧项目值得保留的想法
- 不把所有站点定制塞进一个巨大文件。
- 不同功能可以独立启用、停用。
- 公共入口集中加载模块,便于知道当前启用了什么。
- 把可复用代码从具体主题中抽离。
这些目标仍然成立,但“把很多代码片段 include 起来”还不等于可靠插件。现代实现还需要权限、nonce、命名冲突、升级、卸载、测试和依赖边界。
一个小型站点插件结构
site-features/
├── site-features.php
├── src/
│ ├── Admin.php
│ ├── ContentTypes.php
│ └── Security.php
├── uninstall.php
└── readme.txt
入口文件:
<?php
/**
* Plugin Name: Site Features
* Description: 当前站点的内容类型与业务功能。
* Version: 1.0.0
* Requires PHP: 8.1
* Text Domain: site-features
*/
defined( 'ABSPATH' ) || exit;
require_once __DIR__ . '/src/ContentTypes.php';
require_once __DIR__ . '/src/Admin.php';
require_once __DIR__ . '/src/Security.php';
add_action(
'plugins_loaded',
static function (): void {
Site_Features_Content_Types::register();
Site_Features_Admin::register();
Site_Features_Security::register();
}
);
示例使用长前缀避免全局类名冲突。项目规模更大时可以使用 Composer PSR-4 自动加载,但新增依赖前要确认部署环境能够执行并提交构建产物或安装生产依赖。
模块注册,而不是执行副作用
<?php
final class Site_Features_Admin {
public static function register(): void {
add_action(
'admin_menu',
array( self::class, 'add_menu' )
);
}
public static function add_menu(): void {
// 注册菜单,不在文件加载时直接输出。
}
}
每个模块公开一个 register(),只负责挂接 hook。这样测试可以先加载类,再决定是否注册;代码阅读者也能从入口看清生命周期。
功能开关的层级
- 构建级: 某部署永远不需要的模块,不加载文件。
- 站点级: 由受权管理员配置,值存在 Options API。
- 用户级: 个人偏好存在 User Meta。
- 请求级: 由 capability、页面条件或业务状态决定。
开关不是权限。即使后台不显示模块入口,服务端处理器仍必须检查 capability 和 nonce。
数据与卸载
停用插件应该停止功能,但通常不删除数据;卸载才根据明确策略清理。重要业务数据默认保留,并在文档中说明如何导出和手动删除。
数据库结构升级需要版本号、可重入迁移和失败恢复。不要在每次普通请求无条件运行重建逻辑。
现代化旧仓库时的检查
- 删除已废弃或从 PHP 移除的 API,例如
create_function()。 - 所有输入按语义验证和清洗,所有输出靠近渲染点转义。
- 所有写操作检查 capability、nonce 和对象级权限。
- REST 路由提供真实
permission_callback。 - 后台、前台、Cron、CLI 和 AJAX 有清楚入口。
- 外部请求设置超时、错误处理和敏感日志边界。
- 给关键 hook、迁移和卸载行为补自动化测试。
- 声明并验证 WordPress/PHP 最低支持版本。
旧仓库可以作为个人学习记录,但项目状态应写清楚。读者需要知道它是历史代码、实验项目还是持续维护的软件,这比一句“代码模块化”更重要。