1.2 插件目录结构与文件命名规范
把文件放对位置,后缀选对,Discuz! 才认你的插件。
1.2.1 插件放在哪:source/plugin/{唯一标识符}/
所有插件代码都集中在 source/plugin/ 下,每个插件一个子目录,目录名就是你在后台填的唯一标识符(identifier)。
source/plugin/
├── myplugin/ ← 唯一标识符就是 myplugin
│ ├── index.php
│ ├── core.inc.php
├── otherplugin/
标识符一旦定了就不能改,因为后台菜单、模块定义、钩子类名、XML 清单里的 identifier 字段全指着它。命名规则跟 PHP 变量一致:字母或下划线开头,只含字母、数字、下划线,全站唯一。
X5.0 提供了 DISCUZ_PLUGIN() 常量代替硬编码路径:
require_once DISCUZ_PLUGIN('myplugin').'/core.inc.php'; // 等价于 source/plugin/myplugin/core.inc.php
同系列还有 DISCUZ_TEMPLATE()(代替 /template/xxx 目录)、DISCUZ_DATA(代替 /data 目录)、DISCUZ_ROOT_STATIC(对外展示静态文件的目录)。涉及前端调用的 URI 不用改,后台目录里写死路径的地方则必须改。
1.2.2 后缀就是约定
Discuz! 不靠配置文件说明哪个文件干什么,它靠后缀。这套约定是硬性的,写错了文件就不会被加载:
| 后缀 | 用途 | 放在哪 |
|---|---|---|
.php |
可直接通过浏览器访问的普通程序文件 | 插件根目录 |
.inc.php |
被普通程序文件引用的程序文件 | 插件根目录 |
.func.php |
函数库 | 插件根目录 |
.class.php |
类库 | 插件根目录 |
.htm |
模板文件 | template/ 子目录 |
.htm(手机版) |
手机版模板 | template/mobile/ 子目录 |
.lang.php |
语言包 | 开发时放 data/plugindata/{标识}.lang.php |
table_{表名}.php |
插件自己的 Table 类 | table/ 子目录 |
.class.php 结尾的脚本类对应「程序脚本」模块,.inc.php 结尾的对应「扩展项目」。后缀直接决定后台能不能识别成模块。
1.2.3 我想实现 X,该放哪个文件
按实际需求倒推,动手前查一遍:
| 我要做的事 | 文件放哪 | 用什么后缀 |
|---|---|---|
| 能被浏览器直接打开的独立页面 | 插件根目录 | .php |
| 被主程序 include 的业务逻辑 | 插件根目录 | .inc.php |
| 一堆可复用的函数 | 插件根目录 | .func.php |
钩子类(plugin_标识_CURSCRIPT) |
插件根目录 | .class.php |
| 普通版页面模板 | template/ |
.htm |
| 手机版页面模板 | template/mobile/ |
.htm |
| 界面上的中文文案 | 开发期放 data/plugindata/ |
.lang.php |
| 插件自有数据表的 CRUD | table/ |
table_{表名}.php |
| 运行时生成的缓存或锁 | 站点 data/cache/ |
按需 |
1.2.4 一棵完整的插件目录树
以标识符 myplugin 为例:
source/plugin/myplugin/
├── index ← 空 index 文件,防目录浏览
├── core.inc.php ← 主业务逻辑
├── admin.inc.php ← 后台管理页脚本
├── myplugin.func.php ← 函数库
├── myplugin.class.php ← 钩子类
├── install.php / uninstall.php / upgrade.php
├── template/
│ ├── admin.htm ← 与 admin.inc.php 同名
│ └── mobile/
│ └── myplugin.htm ← 手机版模板
├── table/table_myplugin_log.php
├── adv/adv_myadv.php ← 广告类扩展(可选)
├── magic/magic_mymagic.php ← 道具类扩展(可选)
└── task/task_mytask.php ← 任务类扩展(可选)
data/plugindata/myplugin.lang.php ← 开发时的语言包
data/cache/ ← 动态缓存
data/xxx.sql ← SQL 备份
template/下的模板必须与脚本同名,这是「扩展项目」自动找到模板的前提。adv/、magic/、task/目录名不能改,X3.0 起脚本固定放这三个位置。- 空
index文件不带扩展名,内容为空即可。不认空文件的服务器,放一行<?php exit;?>也行。
1.2.5 Table 类:插件自己的数据层
table/ 下的 table_{表名}.php 是插件自定义表的模型层,调用方式和系统内置的 Table 类一致。假设插件有张日志表 myplugin_log:
<?php
// source/plugin/myplugin/table/table_myplugin_log.php
class table_myplugin_log extends DB_TABLE {
public static function add_log($data) {
return DB::insert('myplugin_log', $data);
}
}
调用方式:
C::t('myplugin_log')->add_log(['uid' => $_G['uid'], 'time' => TIMESTAMP]);
DB::table('myplugin_log'); // 带前缀的完整表名,例如 pre_myplugin_log
DB::table() 会自动加上安装时设定的前缀(官方文档写作 cdb_,X5.0 数据字典写作 pre_)。不要硬写表前缀,用户改过前缀的站点会直接崩。
1.2.6 关于语言包的位置
开发阶段,语言包写在 data/plugindata/{标识}.lang.php,打包发布时并入 XML 清单,安装时由系统释放。
广告类、道具类、任务类的语言包不走这个机制,各有固定位置:
| 扩展类 | 语言包位置 | 附件 |
|---|---|---|
| 广告类 | source/language/adv/lang_{name}.php |
同名 .gif |
| 道具类 | source/language/magic/lang_{name}.php |
.small.gif / .gif |
| 任务类 | source/language/task/lang_{name}.php |
.gif |
X5.0 的模板语言包还支持 i18n 扩展,路径形如 /template/i18n/SC_UTF8/lang_template.php。
1.2.7 命名规范的实际影响
两个约定值得强调,因为违反后 debug 很痛苦:
第一,模板与脚本同名。后台定义「扩展项目」模块时脚本填 admin.inc.php,系统就去找 template/admin.htm。名字对不上,页面白屏或模板为空,新手最常撞这堵墙。
第二,Table 类文件名与表名严格对应。表 myplugin_log 对应文件必须是 table_myplugin_log.php,类名必须是 table_myplugin_log。
运行时缓存写在站点 data/cache/ 下,用 DISCUZ_DATA.'cache/xxx.php' 拼路径。别往插件目录写运行时文件——该目录可能不可写,也可能随打包被覆盖。
1.2.8 本节要点
- 插件根目录
source/plugin/{唯一标识符}/,标识符不可改。 - 后缀决定行为:
.php可直访,.inc.php被引用,.func.php函数库,.class.php钩子类。 - 模板放
template/,手机版放template/mobile/,且必须与脚本同名。 - Table 类放
table/,命名table_{表名}.php,表前缀一律走DB::table()。 - 目录里放个空
index文件防目录浏览;运行时缓存写站点data/cache/。
下一篇讲运行环境:class_core 都干了什么,$_G 里有哪些能直接用的值。
