admin 发表于 2025-7-2 17:00:16

Discuz 插件开发【旗舰版】 二次开发 - 在线手册


验证插件二次开发
本功能由PHP+MYSQL语言开发,无缝对接新秀网络验证系统,采用独立入口文件、类文件、第三方类文件库进行调用开发。所有功能实现及控制均采用api接口操作,数据输出形式为JSON、XML两种常见形式,达到毫秒级数据快速交换功能。1、插件开发是结合新秀网络验证系统进行的二次开发,不能独立使用;2、插件开发遵循Discuz、新秀网络验证、PHP语言规范开发;3、插件开发适用于新秀网络验证系统旗舰版ver1.0版本以后;4、本功能与新秀网络验证系统、扩展框架系统,代码相互独立,但功能同时执行;5、插件开发可使用Discuz内置函数、新秀网络验证内置函数、PHP函数。
适用环境:php版本>7.1、Mysql版本>5.7以上、Discuz版本>3.5。
= Discuz资料库= Discuz插件开发文档= 开发交流=BUG反馈=QQ客服:3188639QQ交流群:281079920


接口开发常用函数
1、DB数据库操作函数    /*常用MYSQL数据库操作命令,增、减、改、查功能函数。*/
2、新秀网络验证数据库对照表    /*新秀网络验证系统内部,所有数据库表及说明*/
3、Discuz官方数据库对照表/*Discuz官方数据库表,包括discuz、UCente数据库字典。*/
4、Discuz基础框架、类、函数    /*包含Discuz官方基础框架、类、函数、方法使用说明。*/
5、新秀网络基础框架类、函数    /*包含新秀网络验证基础框架、类、函数、方法使用说明。*/


使 用 简 介
1、插件开发演示文件下载(IP地址查询)

2、插件开发演示文件目录说明
xinxiuvip_network_plugin \\新秀网络验证应用中心插件主目录—lib \\自定义类存放目录 (用于开发者存放第三方类、自定义类)——lib_ip.php \\自定义方法库开发文件 (自定义开发接口方法库)—function \\自定义方法存放目录 (用于开发者存放自定义开发接口类和方法)——function_ip.php \\自定义方法类开发文件 (自定义开发接口方法类)—plugins \\插件数据库代码存放目录 (用于开发者存放插件数据库执行代码)——ip\\插件名目录———extend_network.php \\设置插件数据库执行代码文件(数据新增、更新、删除,接口增加、删除功能,插件安装和卸载代码)—template/plugin/admin \\插件前端htm开发存放目录(根据discuz官方插件前台开发代码进行开发)——ip\\插件名目录———ip.htm \\插件后台设置文件(根据discuz官方插件后台开发代码进行开发,并在插件设计页面自行增加插件设置文件入口)—ip.inc.php \\自定义接口入口开发文件
3、如何安装、开发、调试?
#1、下载演示文件后,上传至新秀网络验证应用中心插件根目录。discuz论坛插件根目录 \source\plugin\xinxiuvip_network_plugin\#2、使用PHP代码编辑软件,对入口文件 ip.inc.php 和 自定义方法类文件 function_ip.php 等进行开发。#3、如需新增接口文件,请根据演示文件,修改 “ip” 为您想要的文件名,但不能与新秀框架插件内文件名冲突,且开发的接口方法,不能与新秀框架冲突。#4、如何发布自己开发的插件并获取一定收益,请联系客服QQ:3188639#5、功能代码开发完成,组装调试接口://根据演示文件中组装url接口地址http://demo35.xinxiuvip.com/plugin.php?id=xinxiuvip_network_plugin:ip&action=ip_cha&key=123456&ip=14.21.97.153{
      "code": 200,
      "result": "OK",
      "count": 2,
      "data": {
                "ip": "14.21.97.153",
                "dizhi": "- 广东"
      },
      "sqltime": "0.027s",
}

ip.inc.php入口文件代码实例
<?php
#========================================
#以下两行代码,用于开发环境下代码错误提示,可根据提示信息进行代码检查,无误后即可注释掉!
//ini_set("display_errors", "on");
//error_reporting(E_ALL);
#========================================

#========================================
#防止游客直接输入网址进行访问,权限检测,勿动!!!
if (!defined('IN_DISCUZ')) {
    exit('Access Denied');
}
#========================================

#========================================
#引入自定义开发类文件,此文件保存位置 'function/ip' 代表 function/function_ip.php 如需修改请按格式进行!
C::import('function/ip', 'plugin/xinxiuvip_network_plugin', false);//注意这里插件开发和接口二次开发不同!
$api = new function_ip();//实例化function_ip.php类操作,实例化后可用$api->调用function_ip.php中的类方法!
#========================================


#此处为入口文件,根据接口方法,调用对应类方法。
switch ($api->action) { //$api->action 获取入口方法,勿动!!!

    #========================================
    case 'ip_cha': // 判断是否是此方法,如果是则进入下一步!
      $ip =web_client::safe_check('ip', true); //判断参数传输,params为参数名称,iscore为是否是必填项(true为必填,false为可空)
      $api->ip_cha($ip);//调用function_ip.php实例化后类方法,根据类方法设置传入参数。
      break;
    #此处代码根据接口开发需求,可以无限添加循环。
    #========================================

    #========================================
    #此处代码是判断接口输入是否正确。勿删!!!
    default:
      web_output::json_output(400,'error010');
      break;
    #========================================
}



function_ip.php自定义方法类代码实例
<?php
#========================================
#以下两行代码,用于开发环境下代码错误提示,可根据提示信息进行代码检查,无误后即可注释掉!
//ini_set("display_errors", "on");
//error_reporting(E_ALL);
#========================================

#========================================
#防止游客直接输入网址进行访问,权限检测,勿动!!!
if (!defined('IN_DISCUZ')) {
    exit('Access Denied');
}
#========================================

#========================================
#引入核心系统类,勿删!!!
C::import('class/plugin','plugin/xinxiuvip_network_plugin',false);
#========================================


#如何引入自定义系统类?
#1、将自定义核心类、第三方核心类上传至extend文件夹下;
#2、通过 C::import 、 require_once 两种引入形式引入第三方核心类;
#3、在下方方法中使用 lib_ip::convertip_tiny($ip, $ipdatafile); 进行调用,具体功能看lib_ip封装代码;
C::import('lib/ip', 'plugin/xinxiuvip_network_plugin', false);//如果没有自定义类库,请将此处注释掉!
#注释结束


class function_ip extends class_plugin
{
    public $action_all = array('ip_cha');//所有接口必填,用逗号隔开!!!除以下key、adminkey所包含以外,未包含的接口都是通过token令牌进行访问。
    public $function_action_key = array('ip_cha');//使用key密钥访问的接口
    public $function_action_adminkey = array();//使用管理密钥adminkey访问的接口

    /**
   * 构造方法 __construct() 是在实例化对象时被自动调用
   * 用途:可以用于初始化程序(可以给成员属性赋值,也可以调用成员方法)
   */
    public function __construct(){
      $this->plugin_config;//是后台插件设置的数据,可在本页面进行调用。
      parent::__construct();//注意这里和接口二次开发不同
    }

    #========================================
    #此处为标准类方法,可根据DISCUZ、新秀网络验证内置函数进行调用,也可使用PHP官方函数进行操作。
    public function ip_cha($ip){

      #-具体DISCUZ内置函数、新秀网络验证内置函数、PHP函数,论坛相应帖子有专门介绍。
      $tinyipfile = DISCUZ_ROOT.'./data/ipdata/tinyipdata.dat';

      $data = lib_ip::convertip_tiny($ip,$tinyipfile);//调用lib自定义封装类库示例;
      
      $data_array = array(
            'ip'=>$ip,
            'dizhi'=>$data,
      );


      #========================================
      #嵌入插件开发钩子,注意:此扩展方法在应用中心扩展文件中使用‘extend_plugin.php’;
      web_client::class_hook('hook_ip_cha',array('ip'=>$ip));

      /*
         * 下面是插件拓展对应的方法
         * 注意class_hook中的'hook_ip_cha' 就是扩展文件中的方法名!
         *
      public function hook_ip_cha($ip){
            var_dump($ip);
      }
      */
      #========================================



      $data ? web_output::json_output(200,'OK',$data_array): web_output::json_output(400,'请检查IP地址是否正确!');
      #-具体DISCUZ内置函数、新秀网络验证内置函数、PHP函数,论坛相应帖子有专门介绍。
    }
    #========================================

    /**
   * 析构方法 __destruct() 是在对象被销毁时自动调用
   * 用途:可以进行资源的释放操作或文件的关闭操作或信息保存操作
   */
    public function __destruct()
    {
      return parent::__destruct(); // TODO: 析构方法
    }

}


lib_ip.php自定义方法类库代码实例
<?php
#以下两行代码,用于开发环境下代码错误提示,可根据提示信息进行代码检查,无误后即可注释掉!
//ini_set("display_errors", "on");
//error_reporting(E_ALL);
#注释结束

#引入验证自带核心系统类,勿删!!!
C::import('class/xinxiu', 'plugin/xinxiuvip_network', false);
#注释结束

class lib_ip
{

    /**
   * 自定义方法类库,用于扩展系统功能!
   * 1、在function_ip.php中引用此类库,直接调用方法即可!
   * 2、引用代码:C::import('lib/ip', 'plugin/xinxiuvip_network_plugin', false);//引用自定义类库!
   * 3、此方法为纯静态方法,无需实例化,可直接调用!
   * 4、调用代码:lib_ip::convertip_tiny($ip,$tinyipfile);//调用lib自定义封装类库示例!
   */
      public static function convertip_tiny($ip, $ipdatafile) {

      static $fp = NULL, $offset = array(), $index = NULL;

      $ipdot = explode('.', $ip);
      $ip    = pack('N', ip2long($ip));

      $ipdot = (int)$ipdot;
      $ipdot = (int)$ipdot;

      if($fp === NULL && $fp = @fopen($ipdatafile, 'rb')) {
            $offset = @unpack('Nlen', @fread($fp, 4));
            $index= @fread($fp, $offset['len'] - 4);
      } elseif($fp == FALSE) {
            return'- Invalid IP data file';
      }

      $length = $offset['len'] - 1028;
      $start= @unpack('Vlen', $index[$ipdot * 4] . $index[$ipdot * 4 + 1] . $index[$ipdot * 4 + 2] . $index[$ipdot * 4 + 3]);

      for ($start = $start['len'] * 8 + 1024; $start < $length; $start += 8) {

            if ($index[$start] . $index[$start + 1] . $index[$start + 2] . $index[$start + 3] >= $ip) {
                $index_offset = @unpack('Vlen', $index[$start + 4] . $index[$start + 5] . $index[$start + 6] . "\x0");
                $index_length = @unpack('Clen', $index[$start + 7]);
                break;
            }
      }

      @fseek($fp, $offset['len'] + $index_offset['len'] - 1024);
      if($index_length['len']) {
            return '- '.@fread($fp, $index_length['len']);
      } else {
            return '- Unknown';
      }

    }
}



ip.htm插件后台设置文件
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>插件设置页面</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="/source/plugin/xinxiuvip_network/template/res/layui/css/layui.css" rel="stylesheet">
    <link href="/source/plugin/xinxiuvip_network/template/res/adminui/dist/css/admin.css" rel="stylesheet">
</head>
<style>
    body {
      background-color: #ffffff;
    }
</style>

<body>

    <div class="layui-fluid">
      <div class="layui-main">
            <blockquote class="layui-elem-quote">
                此页面为插件后台设置页面,请根据具体文档进行设置。
                <a href="http://www.xinxiuvip.com/forum.php?mod=viewthread&tid=649" target="_blank">【插件开发文档】</a>
            </blockquote>
            <div class="layui-form" id="form" lay-filter="form" action="">


                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">radio开关类</label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <!--{if $plugin_config['status'] == 0}-->
                        <input name="status" type="radio" value="0" checked="" title="正常" lay-skin="primary">
                        <input name="status" type="radio" value="1" title="禁止" lay-skin="primary">
                        <input name="status" type="radio" value="2" title="其他" lay-skin="primary">
                        <!--{elseif $plugin_config['status'] == 1}-->
                        <input name="status" type="radio" value="0" title="正常" lay-skin="primary">
                        <input name="status" type="radio" value="1" checked="" title="禁止" lay-skin="primary">
                        <input name="status" type="radio" value="2" title="其他" lay-skin="primary">
                        <!--{elseif $plugin_config['status'] == 2}-->
                        <input name="status" type="radio" value="0" title="正常" lay-skin="primary">
                        <input name="status" type="radio" value="1" title="禁止" lay-skin="primary">
                        <input name="status" type="radio" value="2" checked="" title="其他" lay-skin="primary">
                        <!--{/if}-->
                  </div>
                  <div class="layui-form-mid layui-word-aux">radio开关类注释</div>
                </div>

                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">text文本</label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <input type="text" name="text" value="{$plugin_config['text']}" autocomplete="off"
                            class="layui-input" style="width: 99%;">
                  </div>
                  <div class="layui-form-mid layui-word-aux">text文本注释</div>
                </div>

                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">text文本验证</label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <input type="password" name="password" value="{$plugin_config['password']}" autocomplete="off"
                            class="layui-input" style="width: 99%;">
                  </div>
                  <div class="layui-form-mid layui-word-aux">text文本验证注释</div>
                </div>

                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">
                        textarea文本
                  </label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <textarea class="layui-textarea" name="textarea" style="width: 99%;">{$plugin_config['textarea']}
                                                </textarea>
                  </div>
                  <div class="layui-form-mid layui-word-aux">
                        textarea文本域注释
                  </div>
                </div>
                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">select下拉框</label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <select name="select">
                            <option value="">请选择</option>
                            <!--{if $plugin_config['select'] == 1}-->
                            <option value="1" selected>value为1</option>
                            <!--{else}-->
                            <option value="1">value为1</option>
                            <!--{/if}-->
                            <!--{if $plugin_config['select'] == 2}-->
                            <option value="2" selected>value为2</option>
                            <!--{else}-->
                            <option value="2">value为2</option>
                            <!--{/if}-->
                            <!--{if $plugin_config['select'] == 3}-->
                            <option value="3" selected>value为3</option>
                            <!--{else}-->
                            <option value="3">value为3</option>
                            <!--{/if}-->
                        </select>
                  </div>
                  <div class="layui-form-mid layui-word-aux">select下拉框注释</div>
                </div>
                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">时间选择</label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <input type="text" name="groupline" value="{$plugin_config['groupline']}"
                            id="form-date" placeholder="yyyy-MM-dd HH:mm:ss" autocomplete="off"
                            class="layui-input" style="width: 99%;">
                  </div>
                  <div class="layui-form-mid layui-word-aux">时间选择注释</div>
                </div>

                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label">
                        选择积分
                  </label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <select name="credits">
                            {$plugin_config['credits']}
                        </select>
                  </div>
                  <div class="layui-form-mid layui-word-aux">
                        内置选择积分类型。
                  </div>
                </div>
                <div class="layui-form-item adminj-sort-item" style="width: 100%;">
                  <label class="layui-form-label" style="margin-top: -3px;">
                        选择会员组
                  </label>
                  <div class="layui-input-inline" style="width: 30%;">
                        <select name="groupids" lay-ignore="" class="layui-select-group layui-select-tips"
                            multiple="multiple" data-placeholder="" data-dropdown-css-class="select2-purple"
                            style="width: 99%;height: 200px;">
                            {$plugin_config['groupids']}
                        </select>
                  </div>
                  <div class="layui-form-mid layui-word-aux">
                        内置选择会员组。
                  </div>
                </div>
                <div class="layui-form-item adminj-sort-item">
                  <div class="layui-input-block">
                        <button type="submit" class="layui-btn" lay-submit="" lay-filter="postButton" id="postButton">
                            立即修改
                        </button>
                  </div>
                </div>
            </div>
      </div>
    </div>
    <script src="/source/plugin/xinxiuvip_network/template/res/layui/layui.js?t=1"></script>


    <script>
      layui.use(['form', 'layer', 'jquery'], function () {
            var form = layui.form;
            var layer = layui.layer;
            var $ = layui.jquery;
            var laydate = layui.laydate;

            laydate.render({
                elem: '#form-date',
                type: 'datetime',
                fullPanel: true // 2.8+
            });

            form.on('submit(postButton)', function (data) {
                function getQueryParam(param) {
                  const search = window.location.search || window.location.hash.split('?') || '';
                  const params = new URLSearchParams(search);
                  return params.get(param);
                }
                const dir = getQueryParam('dir');
                $.post('plugin.php?id=xinxiuvip_network_plugin:web_admin&action=api&dir=' + dir, {
                  data: data.field,
                }, function (jsondata, textStatus, jqXHR) {
                  if (jsondata.code === 200) {
                        layer.msg('[ ' + dir + ' ] 插件设置成功!', {
                            icon: 1
                        });
                  } else {
                        layer.msg('[ ' + dir + ' ] 插件设置失败!', {
                            icon: 2
                        });
                  }
                  return false;
                });

            });
      });
    </script>
</body>

</html>



extend_network.php插件安装卸载配置实例
<?php

if (!defined('IN_DISCUZ')) {
    exit('Access Denied');
}

C::import('class/plugin', 'plugin/xinxiuvip_network_plugin', false); //引入系统核心类

class extend_network extends class_plugin
{

    public function __construct() //构造方法
    {
      parent::__construct(false);
    }
    /**
   * ##创建插件的安装代码
   * 1、此处代码以创建数据库表为主,提前创建插件需要的数据库表及字段!
   * 2、在这里也可以添加一些插件自定义php代码,系统自动执行!
   * 3、如果不需要执行代码,直接返回true即可,实例:return true;
   */
    public function hook_plugin_install() //插件安装代码
    {
      $sql = <<<EOF
      CREATE TABLE IF NOT EXISTS `pre_xinxiuvip_plugin_ip` (
          `id` BIGINT(16) unsigned NOT NULL AUTO_INCREMENT,
          `text1` longtext NOT NULL,
          PRIMARY KEY (`id`)
      ) ENGINE=InnoDBDEFAULT CHARSET=utf8 ;
EOF;
      $sql ? web_mysql::xinxiuvip_plugin_run_sql($sql) : ''; //执行上面的多行SQL代码;
      return true; //创建结束
    }
    /**
   * ##更新插件的更新代码
   * 1、此处代码以更新数据库表为主,对安装的数据库表和字段进行更新!
   * 2、在这里也可以添加一些插件自定义php代码,系统自动执行!
   * 3、如果不需要执行代码,直接返回true即可,实例:return true;
   */
    public function hook_plugin_upgrade() //插件更新代码
    {
      $sql = <<<EOF
      CREATE TABLE IF NOT EXISTS `pre_xinxiuvip_plugin_ip` (
          `id` BIGINT(16) unsigned NOT NULL AUTO_INCREMENT,
          `text1` longtext NOT NULL,
          PRIMARY KEY (`id`)
      ) ENGINE=InnoDBDEFAULT CHARSET=utf8 ;
EOF;
      $sql ? web_mysql::xinxiuvip_plugin_run_sql($sql) : ''; //执行上面的多行SQL代码;
      return true; //更新结束
    }
    /**
   * ##卸载插件的卸载代码
   * 1、此处代码以卸载数据库表为主,对安装的数据库表和字段进行卸载!
   * 2、在这里也可以添加一些插件自定义php代码,系统自动执行!
   * 3、如果不需要执行代码,直接返回true即可,实例:return true;
   */
    public function hook_plugin_uninstall() //插件卸载代码
    {
      $sql = <<<EOF
      DROP TABLE IF EXISTS `pre_xinxiuvip_plugin_ip`;
EOF;
      $sql ? web_mysql::xinxiuvip_plugin_run_sql($sql) : ''; //执行上面的多行SQL代码;
      return true; //更新结束
    }
    /**
   * ##安装插件的接口代码
   * 1、此处代码以添加插件接口为主,对插件封装的接口,添加到数据库!
   * 2、如果不需要添加任何接口信息,直接返回false即可,实例:return false;
   */
    public function hook_plugin_actions_install() //插件安装接口代码
    {
      $actions_array = array(
            array(
                'type' => 'ip模块',//插件模块
                'name' => '查询ip地址',//接口名称
                'actions' => 'ip_cha',//接口方法
                'url' => 'http://www.xinxiuvip.com/thread-556-1-1.html',//接口文档
            ),
            array(
                'type' => 'ip模块1',//插件模块
                'name' => '查询ip地址1',//接口名称
                'actions' => 'ip_cha1',//接口方法
                'url' => 'http://www.xinxiuvip.com/thread-556-1-1.html',//接口文档
            ),
      );
      return $actions_array;
    }
    /**
   * ##更新插件的接口代码
   * 1、此处代码以更新插件接口为主,对插件封装的接口,更新到数据库!
   * 2、如果不需要更新任何接口信息,直接返回false即可,实例:return false;
   */
    public function hook_plugin_actions_upgrade() //插件接口更新代码
    {
      $actions_array = array(
            array(
                'type' => 'ip模块2',//插件模块
                'name' => '查询ip地址2',//接口名称
                'actions' => 'ip_cha2',//接口方法
                'url' => 'http://www.xinxiuvip.com/thread-556-1-1.html',//接口文档
            ),
      );
      return $actions_array;
    }
}


页: [1]
查看完整版本: Discuz 插件开发【旗舰版】 二次开发 - 在线手册