在开始编写DzzOffice插件之前,您应当首先对插件开发流程有一个大致的了解。以下是推荐的插件开发流程:在熟练掌握DzzOffice系统的基础上,对希望完善或补充的个性化功能进行评估,进而提出插件的功能需求。
对插件进行概要设计,如:需使用哪些菜单、参数,配置哪些选项、数据结构设计、前后台实现哪些功能等。
阅读本文档并在系统设置中实际体验DzzOffice插件接口所实现的功用,如:如何设计插件以便顺利接入官方应用市场,插件接口能实现哪些功能、不能实现哪些功能,以及插件为此所需的优化、改造和取舍。
编写相应程序代码和模板语句,实现所需的功能并进行代码测试、兼容性测试和代码改进。
如有必要公开插件,可将其配置信息导出为XML文件,并与相应程序和模板文件一同打包。同时,编写一份适用于新手的插件说明书也是必不可少的,包括:插件适用的DzzOffice版本、功能概述、兼容性声明、安装方法、使用方法、卸载方法等。
将插件提供给他人或自行使用,根据使用者反馈对插件进行完善。至此,插件开发流程结束。
文件命名规范#
DzzOffice遵循以下规范对程序和模板进行命名,请在设计插件时尽量遵循此命名规范:可直接通过浏览器访问的普通程序文件,以.php为后缀命名。
被普通程序文件或引用程序文件引用的函数库或类库,以function_xxx.php
(函数库)或class_xxx.php
(类库)为命名。
模板文件,以.htm
为后缀命名,插件模板文件位于dzz/identifier/template/
目录中,移动端插件模板位于dzz/identifier/template/mobile/
目录中。
模板语言包文件,以.php
为后缀命名,插件语言包文件位于dzz/identifier/language/zh-cn/
目录中,文件名为lang.php
。
动态缓存文件,存放于/data/cache
目录中,根据不同的用途进行独立命名。
使用后台数据备份功能生成的备份文件,通常以.sql为
后缀,存放于data/
目录中。
数据表的文件统一命名为Table类,通过"C::t(Table类文件名)
"方式调用。插件如需封装自己的数据表,可将Table类文件存放于dzz/identifier/class/table/
目录下,并以table_表名.php
格式命名。
唯一标识符用于在后续的插件模块中调用本插件,必须确保不与现有插件重复。命名规则应遵循与 PHP 变量命名相同的规范。尽管在初次设置后仍可进行更改,但强烈建议一次性完成此配置设置,以免引发大量的代码变更并增加编码的复杂性。请注意:唯一标识符请勿设置得过短,或使用可能导致与其他插件重复的命名。例如插件名称为“小胡插件”,则唯一标识符可设置为“xiaohu”。最后,在dzz/目录中创建与唯一标识符同名的目录名,如 dzz/xiaohu/。
class_core.php 模块功能#
core/class/class_core.php
是 DzzOffice 的通用初始化模块程序,其几乎被所有的外部代码所引用,在您开始插件设计之前,可以先对该模块的大致功能做一定的了解。class_core.php
主要完成了以下任务:对不同 PHP 及操作系统环境做了判断和兼容性处理,使得 DzzOffice 可以运行于各种不同配置的服务器环境下。
初始化常量 IN_DZZ 为 TRUE,用于 include 或 require 后续程序的判断,避免其他程序被非法引用。
读取系统程序所在绝对路径,存放于常量 DZZ_ROOT 中。
加载所需的基本函数库 core/function/function_core.php。
通过config/config.php
中提供的数据库账号信息,建立数据库连接。DzzOffice 支持数据表的前缀,如需获得表的全名,可使用DB::table('tablename')方式。
判断用户是否登录,如登录标记$_G['uid']
为非0
,
将$_G['username']
(用户名)、$_G['member']['username']
(原始的用户名,可用于页面显示)、$_G['member']['password']
(用户密码的MD5串)等相应用户信息赋值,其他用户信息存放于 $_G['member']
,更多信息可通过getuserprofile()
获取。
判断系统管理员权限,将系统管理权限标记$_G['adminid']
为非0
。
将用户权限按照其所在的主用户组 ID 标记为$_G['groupid']
,相关权限从该$_G['groupid']
所对应的系统缓存中读出,存放于 $_G['group']
。
代码规范#
文件开头#
PHP 标签应该使用 <?php
,慎用 <?
和 <?=
。请注意,插件根目录下的所有 *.php
都可通过 index.php?mod=xxx&op=xxx
方式进行访问。对于 PHP
文件,除 API 和第三方 SDK 外,必须检测 IN_DZZ
常量是否已经定义。不需检测 IN_DZZ
常量的情况有:没有使用 DzzOffice 提供的任何类和函数;
手动载入 class_core.php 并初始化 DzzOffice 环境。
如果该文件是后台模块,则应该检测 IN_ADMINCP
常量(后台模块可同时检测 IN_DZZ
、IN_ADMINCP
,或单独仅检测 IN_ADMINCP
)。install.php
、uninstall.php
、upgrade.php
必须检测 IN_ADMINCP
常量。后台入口#
有些用户为了安全会修改后台入口文件名,所以网站后台入口请使用ADMINSCRIPT
,而不是admin.php
。文件结尾#
对于纯 PHP
文件,推荐文件结尾不使用?>
标签。如果文件结尾使用了?>
标签,在标签后不允许存在任何多余的字符,包括空格、回车在内的空白。插件规范#
数据表规范#
定长字符串(如 md5 哈希值)使用 char 而非 varchar 等。
开发者应当合理设置数据表的索引,尤其是作为查询条件的字段。安装和卸载#
安装时创建数据表语句不应指定数据表编码(DzzOffice 会自动将 TYPE=MyISAM
转换为 ENGINE=MyISAM DEFAULT CHARSET=xxx
)。卸载时,应当删除插件安装时建立的所有数据表和字段,完全清理插件使用过程中产生的文件(如保存的用户上传文件)。如果开发者认为插件数据价值较大,可在卸载时向用户询问是否保留数据。如果用户保留数据,下次安装插件时,不能出现无法安装的情况,如产生数据表已存在等错误。效率优化#
开发者在设计插件时,在保证功能完整和安全性的前提下,应多考虑代码的运行效率。
最常见的低效率代码是在循环中执行 SQL 语句,如下面的代码:在大 多数场景下,循环内的 SELECT
和 DELETE
语句均可通过使用 WHERE IN
语句进行优化,使原本的执行多次查询优化为一次。在使用全局嵌入点时必须考虑效率问题:决不允许在循环中执行 SQL 查询;查询条件对应的字段原则上必须建立索引;尽量使用缓存。某些插件需要在嵌入点访问远程 URL,原则上不允许进行这种行为,因为如果访问 URL 耗时较长,会直接拖慢整个页面的加载速度。这种情况应该将远程访问的行为单独放在一个 PHP 脚本中进行,并在嵌入点中使用 script
标签访问,将原本的操作优化为异步进行。开发者应该充分利用缓存功能,并尽可能降低数据库的查询次数,达到效率优化的目的。内置函数#
开发者应当使用以下 DzzOffice 内置函数来取代 PHP 原生函数,提高代码的兼容性:使用dfsockopen()
访问远程 URL,而非使用file_get_contents()
或手动创建CURL
对象
使用dhtmlspecialchars()
过滤 HTML 字符,防止在 PHP 5.4.0 或更高版本的服 务器中出现问题
使用daddslashes()
过滤入库的字符串,正确处理数组变量
使用cutstr()
而非 substr()
切割字符串,避免中文字符被错误切割
安全规范#
include/require#
如果使用 include
、include_once
、require
、require_once
动态的引入文件,必须保证路径的合法性,例如下面的代码:变量 $mod 未经过滤就被直接使 用,存在重大的安全隐患。开发者必须添加相应的过滤代码(如 in_array($mod, array ('index', 'del'))
),保证类似情况下,$mod
是合法的。SQL 注入#
避免 SQL 注入漏洞的原则是所有数据在入库前都必须进行过滤,如数字型ID执行intval()
,字符串数据执行 daddslashes()
。为避免代码逻辑复杂而导致部分地方遗漏过滤,建议使用数据层功能,将对某一数据表的操作都封装为类方法,统一进行过滤。同时,尽量不要使用类似下面手动生成 SQL 执行的代码:同时还需要注意,如果查询条件是LIKE
语句,还应该额外过滤%
和_
字符。二次入库导致的 SQL 注入问题常被人忽视,如下列代码:此处由于$result['name']
是从数据库中查询得到,未经过滤就再次代入到 SQL 语句中,依旧存在 SQL 注入漏洞,而且较为隐蔽,开发者应当正确处理这种情况。CSRF#
无论前台或后台功能,只要涉及数据变更操作(如插入、修改和删除),都必须检测请求来源,避免 CSRF 跨站请求伪造攻击。请求来源的检测可通过调用submitcheck()
或手动检测FORMHASH
,上述代码if(!submitcheck('delsubmit')) {
或if(FORMHASH != $_GET['formhash']) {
XSS#
插件将数据库中的字符串类型数据显示在页面之前,如果没有经过 dhtmlspecialchars() 过滤,则存在 XSS 跨站脚本攻击漏洞。开发者应当意识到 XSS 漏洞的危害,在数据直接输出前进行过滤。应注意,数据输出除了直接显示外,还包括 <input type="text" value="$data" />
和 <textarea>$data</textarea>
等情况。注意事项#
请在您动手编写插件之前,务必仔细阅读以下原则,并遵循这些原则以避免可能发生的问题:请将所有与插件相关的所有程序(包括前端和后端程序)放置于 dzz/ 目录中。同时在插件的安装说明中指出,插件的文件需要复制到哪些目录。为了避免与其他插件冲突,请尽量建立 dzz/ 下的子目录,并将插件程序放置于子目录下,这样您编写的插件将获得更好的兼容性。
如果您的插件包含“导航栏”模块,该模块将统一用 index.php?mod=xxx&op=yyy 的方式调用,请在相应链接、表单中使用此方式。其中 xxx 为插件的惟一标识符,yyy 为模块名称。前台插件外壳程序 index.php 已经加载了通用初始化模块 /core/class_core.php,不需再次引用。
如果您的插件包含“管理中心”模块,该模块将统一用 index.php?mod=xxx&op=admin 的方式调用,请在相应链接、表单中使用此方式。其中 xxx 为插件的惟一标识符,admin 为模块名称。
请勿绕过插件的前后台外壳(index.php 和 admin.php)而以直接调用某程序的方式编写插件。这种做法会导致用户使用不便、代码冗余和不规范,同时又产生了因验证程序考虑不周到而带来的安全隐患。您可以在任何地方(如链接和表单)方便地使用上述 URL 地址调用插件模块。
由于所有与插件相关的程序(包括前端程序)都使用外壳调用,请务必在第一行添加以下代码: 通常情况下,请使用插件导出的功能发布插件,以便用户一次性导入插件的配置数据。 在极特殊情况下,也可以分步骤告知使用者如何进行插件配置管理和安装此插件。
若功能独立,请尽量使用单独程序的方式编写插件(即外挂型插件),并尽量减少对 DzzOffice 本身代码的修改,这将为使用者今后的升级带来很大方便。
您可以修改 DzzOffice 本身的数据结构,但推荐在不影响效率的前提下将插件数据存储在另外的数据表中,以防您添加的字段、索引与后期版本 DzzOffice 核心数据字段重名。
请在插件说明书中对插件进行详尽描述,如新增字段、表、程序,版本兼容性,后续支持方式(如不提供支持或采用特定方式提供)。若可能,请提供插件的卸载方法,包括去除字段、删除新增程序、恢复被插件修改的程序等。使用者会感激您为此付出的辛勤劳动,甚至愿意支付相应的费用支持您未来的发展。
若插件使用另外的数据表存储,请在插件管理中准确设置插件所使用的数据表名称(不含前缀),以便用户在备份数据时能一并备份插件数据。
文件命名原则上只使用小写英文字母,不建议文件名包含中文,不允许文件名中包含任何特殊字符。
插件目录及所有子目录中都应该包含一个空白的 index.htm 文件,以避免当http服务器的目录列表打开时,服务器文件被索引和列表。