<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel rdf:about="https://dwt.life/feed/rss/tag/thinkphp/">
<title>dwt&#039;s life - thinkphp</title>
<link>https://dwt.life/tag/thinkphp/</link>
<description></description>
<items>
<rdf:Seq>
<rdf:li resource="https://dwt.life/archives/327/"/>
<rdf:li resource="https://dwt.life/archives/188/"/>
</rdf:Seq>
</items>
</channel>
<item rdf:about="https://dwt.life/archives/327/">
<title>thinkphp6 消息队列</title>
<link>https://dwt.life/archives/327/</link>
<dc:date>2023-04-09T21:19:32+08:00</dc:date>
<description>安装composer require topthink/think-queue配置配置文件位于 config/queue.php使用redis作为消息队列&lt;?php 
return [
    &#039;default&#039;     =&gt; &#039;redis&#039;,
    &#039;connections&#039; =&gt; [
        &#039;sync&#039;     =&gt; [
            &#039;type&#039; =&gt; &#039;sync&#039;,
        ],
        &#039;database&#039; =&gt; [
            &#039;type&#039;       =&gt; &#039;database&#039;,
            &#039;queue&#039;      =&gt; &#039;default&#039;,
            &#039;table&#039;      =&gt; &#039;jobs&#039;,
            &#039;connection&#039; =&gt; null,
        ],
        &#039;redis&#039;    =&gt; [
            &#039;type&#039;       =&gt; &#039;redis&#039;,
            &#039;queue&#039;      =&gt; &#039;default&#039;,//默认队列名称
            &#039;host&#039;       =&gt; &#039;127.0.0.1&#039;,//Redis主机IP地址
            &#039;port&#039;       =&gt; 6379,//Redis端口
            &#039;password&#039;   =&gt; &#039;&#039;,//Redis密码
            &#039;select&#039;     =&gt; 1,//Redis数据库
            &#039;timeout&#039;    =&gt; 0,//Redis连接超时时间
            &#039;persistent&#039; =&gt; false,//是否长连接
        ],
    ],
    &#039;failed&#039;      =&gt; [
        &#039;type&#039;  =&gt; &#039;none&#039;,
        &#039;table&#039; =&gt; &#039;failed_jobs&#039;,
    ],
];创建消费类在app目录下创建目录job，创建类文件&lt;?php

namespace app\job;

use think\facade\Log;
use think\queue\Job;

class Test
{
    public function fire(Job $job, $data)
    {
        // 处理业务逻辑返回为true表示消费成功，则删除队列
        if($this-&gt;test($job-&gt;attempts())){
            // 删除队列
            $job-&gt;delete();
        }else{
            // 判断执行失败次数，到达设置值后删除消息队列
            if ($job-&gt;attempts() &gt;= 10) {
                Log::channel(&#039;qxsp&#039;)-&gt;info(&#039;到达规定次数删除了&#039;);
                // 删除队列
                $job-&gt;delete();
            }else{
                Log::channel(&#039;qxsp&#039;)-&gt;info(&#039;继续执行&#039;);
                // 重庆消息队列，重要：如果没有这样设置，默认的是1失败后1分钟执行一次，这样设置的话达到失败后隔多久执行下一次。官方的坑研究了好久。
                $job-&gt;release(120);
            }

        }
    }

    // 处理业务逻辑
    public function test($data)
    {
        Log::channel(&#039;qxsp&#039;)-&gt;info($data);
        return false;
    }
}创建任务类Queue::push($job, $data = '', $queue = null) 和Queue::later($delay, $job, $data = '', $queue = null) 两个方法，前者是立即执行，后者是在$delay秒后执行$job 是任务名命名空间是app\job的，比如上面的例子一,写Job1类名即可其他的需要些完整的类名，比如上面的例子二，需要写完整的类名app\lib\job\Job2如果一个任务类里有多个小任务的话，如上面的例子二，需要用@+方法名app\lib\job\Job2@task1、app\lib\job\Job2@task2$data 是你要传到任务里的参数$queue 队列名，指定这个任务是在哪个队列上执行，同下面监控队列的时候指定的队列名,可不填以下为上面消费类例子public function index()
{
    Queue::push(&#039;Test&#039;, date(&quot;h:i:sa&quot;), &#039;wu&#039;);
}监听任务并执行在根目录下执行使用该语句：修改消费类代码可以实时更新无需重启php think queue:listen使用该语句：修改消费类代码不会实时更新，需要重启才能生效php think queue:workhttps://www.kancloud.cn/w13244855188/think-queue-wu</description>
</item>
<item rdf:about="https://dwt.life/archives/188/">
<title>ThinkPHP5中如何加入插件功能</title>
<link>https://dwt.life/archives/188/</link>
<dc:date>2022-01-31T17:28:00+08:00</dc:date>
<description>加入插件的方法：1、在系统的index.php入口中加入如下配置// 插件目录
define(&#039;ADDON_PATH&#039;, __DIR__ . &#039;/../addons/&#039;);
// 开启系统行为
define(&#039;APP_HOOK&#039;, true);2、在数据库中加入addons插件表和hooks钩子表CREATE TABLE `addons` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT &#039;主键&#039;,
  `name` varchar(40) NOT NULL COMMENT &#039;插件名或标识&#039;,
  `title` varchar(20) NOT NULL DEFAULT &#039;&#039; COMMENT &#039;中文名&#039;,
  `description` text COMMENT &#039;插件描述&#039;,
  `status` tinyint(1) NOT NULL DEFAULT &#039;1&#039; COMMENT &#039;状态&#039;,
  `config` text COMMENT &#039;配置&#039;,
  `author` varchar(40) DEFAULT &#039;&#039; COMMENT &#039;作者&#039;,
  `version` varchar(20) DEFAULT &#039;&#039; COMMENT &#039;版本号&#039;,
  `create_time` int(10) unsigned NOT NULL DEFAULT &#039;0&#039; COMMENT &#039;安装时间&#039;,
  `has_adminlist` tinyint(1) unsigned NOT NULL DEFAULT &#039;0&#039; COMMENT &#039;是否有后台列表&#039;,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=&#039;插件表&#039;;

CREATE TABLE `hooks` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT &#039;主键&#039;,
  `name` varchar(40) NOT NULL DEFAULT &#039;&#039; COMMENT &#039;钩子名称&#039;,
  `description` text NOT NULL COMMENT &#039;描述&#039;,
  `type` tinyint(1) unsigned NOT NULL DEFAULT &#039;1&#039; COMMENT &#039;类型&#039;,
  `update_time` int(10) unsigned NOT NULL DEFAULT &#039;0&#039; COMMENT &#039;更新时间&#039;,
  `addons` varchar(255) NOT NULL DEFAULT &#039;&#039; COMMENT &#039;钩子挂载的插件 &#039;&#039;，&#039;&#039;分割&#039;,
  `status` tinyint(2) DEFAULT &#039;1&#039;,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;3、在ThinkPHP5的根目录中创建addons目录www  WEB项目目录
├─composer.json         composer定义文件
├─README.md             README文件
├─LICENSE.txt           授权说明文件
├─addons                插件目录（新增）
├─application           应用目录
│  ├─common             公共模块目录（可以更改）
│  ├─runtime            应用的运行时目录（可写，可定制）
│  ├─module             模块目录
│  │  └─ ...            更多类库目录
│  ├─common.php         公共函数文件
│  ├─config.php         公共配置文件
│  ├─route.php          路由配置文件
│  └─database.php       数据库配置文件
│
├─public                WEB目录（对外访问目录）
│  ├─index.php          入口文件
│  ├─.htaccess          用于apache的重写
│  └─router.php         快速测试文件（用于PHP内置webserver）
│
├─thinkphp              框架系统目录4、在application中的common.php公共函数里添加两个公共方法/**
 * 获取插件类的类名
 * @param strng $name 插件名
 * @param string $ext 扩展名
 */
function get_addon_class($name, $ext = EXT)
{
    // 初始化命名空间及类名
    $class = &quot;addons\\{$name}\\&quot; . ucfirst($name);
    return $class;
}

/**
 * 处理插件钩子
 * @param string $hook   钩子名称
 * @param mixed $params 传入参数
 * @return void
 */
function hook($hook, $params = [])
{
    // 钩子调用
    \think\Hook::listen($hook, $params);
}5、在application目录下创建common模块，在common模块中创建behavior行为目录，在公共行为目录中创建Hooks.php钩子行为插件，内容如下：&lt;?php
// +----------------------------------------------------------------------
// | zzstudio [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://www.zzstudio.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Byron Sampson &lt;xiaobo.sun@gzzstudio.net&gt;
// +----------------------------------------------------------------------
namespace app\common\behavior;

use think\Hook;

class Hooks
{
    public function run(&amp;$param = [])
    {
        if(defined(&#039;BIND_MODULE&#039;) &amp;&amp; BIND_MODULE === &#039;Install&#039;) return;
        // 动态加入命名空间
        \think\Loader::addNamespace(&#039;addons&#039;, ADDON_PATH);
        // 获取钩子数据
        $data = S(&#039;hooks&#039;);
        if(!$data){
            $hooks = M(&#039;Hooks&#039;)-&gt;getField(&#039;name,addons&#039;);
            // 获取钩子的实现插件信息
            foreach ($hooks as $key =&gt; $value) {
                if($value){
                    $map[&#039;status&#039;]  =   1;
                    $names          =   explode(&#039;,&#039;,$value);
                    $map[&#039;name&#039;]    =   [&#039;IN&#039;, $names];
                    $data = M(&#039;Addons&#039;)-&gt;where($map)-&gt;getField(&#039;id,name&#039;);
                    if($data){
                        $addons = array_intersect($names, $data);
                        Hook::add($key, array_map(&#039;get_addon_class&#039;, $addons));
                    }
                }
            }
            S(&#039;hooks&#039;, Hook::get());
        }else{
            Hook::import($data, false);
        }
    }
}6、在application目录中创建tags.php行为配置文件，并且加入内容&lt;?php
// 系统行为定义
return [
    &#039;app_init&#039; =&gt; [
        &#039;app\\common\\behavior\\Hooks&#039;
    ],
    &#039;action_begin&#039;=&gt; [
    ],
    &#039;app_end&#039;=&gt; [
    ]
];到此为止，插件功能就集成完毕了。接下来我们做个简单的插件试一下：1、在addons目录中创建一个Base.php 插件的基类&lt;?php
// +----------------------------------------------------------------------
// | addons [ WE CAN DO IT JUST ZZSTUDIO IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2015 http://www.zzstudio.net All rights reserved.
// +----------------------------------------------------------------------
// | Author: byron sampson &lt;xiaobo.sun@zzstudio.net&gt;
// +----------------------------------------------------------------------
namespace addons;

/**
 * 插件类
 * @author byron sampson &lt;xiaobo.sun@zzstudio.net&gt;
 */
abstract class Base{
    /**
     * 视图实例对象
     * @var view
     * @access protected
     */
    protected $view = null;

    /**
     * $info = array(
     *  &#039;name&#039;=&gt;&#039;Editor&#039;,
     *  &#039;title&#039;=&gt;&#039;编辑器&#039;,
     *  &#039;description&#039;=&gt;&#039;用于增强整站长文本的输入和显示&#039;,
     *  &#039;status&#039;=&gt;1,
     *  &#039;author&#039;=&gt;&#039;thinkphp&#039;,
     *  &#039;version&#039;=&gt;&#039;0.1&#039;
     *  )
     */
    public $info                =   array();
    public $addon_path          =   &#039;&#039;;
    public $config_file         =   &#039;&#039;;
    public $custom_config       =   &#039;&#039;;
    public $admin_list          =   array();
    public $custom_adminlist    =   &#039;&#039;;
    public $access_url          =   array();

    /**
     * 基类构造函数
     * Base constructor.
     */
    public function __construct()
    {
        $this-&gt;view         =   new \think\View();
        $this-&gt;addon_path   =   ADDON_PATH . $this-&gt;getName() . &#039;/&#039;;
        $TMPL_PARSE_STRING = C(&#039;parse_str&#039;);
        $TMPL_PARSE_STRING[&#039;__ADDONROOT__&#039;] = $TMPL_PARSE_STRING[&#039;__ADDONS__&#039;] . &#039;/&#039; . $this-&gt;getName();
        C(&#039;parse_str&#039;, $TMPL_PARSE_STRING);
        if(is_file($this-&gt;addon_path . &#039;config.php&#039;)){
            $this-&gt;config_file = $this-&gt;addon_path . &#039;config.php&#039;;
        }
    }

    /**
     * 模板主题设置
     * @access protected
     * @param string $theme 模版主题
     * @return Action
     */
    final protected function theme($theme)
    {
        $this-&gt;view-&gt;theme($theme);
        return $this;
    }

    /**
     * 模板变量赋值
     * @access protected
     * @param mixed $name 要显示的模板变量
     * @param mixed $value 变量的值
     * @return Action
     */
    final protected function assign($name, $value=&#039;&#039;)
    {
        $this-&gt;view-&gt;assign($name,$value);
        return $this;
    }


    //用于显示模板的方法
    final protected function fetch($templateFile = CONTROLLER_NAME)
    {
        if(!is_file($templateFile)){
            $templateFile = $this-&gt;addon_path . $templateFile . &quot;.html&quot;;
            if(!is_file($templateFile)){
                E(L(&#039;_TEMPLATE_NOT_EXIST_&#039;) . &quot;:$templateFile&quot;);
            }
        }
        return $this-&gt;view-&gt;fetch($templateFile);
    }

    /**
     * 获取插件名
     * @return string
     */
    final public function getName()
    {
        $class = get_class($this);
        list($space, $name, $class) = explode(&#039;\\&#039;, $class);

        return $name;
    }

    /**
     * 检查配置信息是否完整
     * @return bool
     */
    final public function checkInfo()
    {
        $info_check_keys = array(&#039;name&#039;,&#039;title&#039;,&#039;description&#039;,&#039;status&#039;,&#039;author&#039;,&#039;version&#039;);
        foreach ($info_check_keys as $value) {
            if(!array_key_exists($value, $this-&gt;info))
                return false;
        }
        return true;
    }

    /**
     * 获取插件的配置数组
     */
    final public function getConfig($name = &#039;&#039;)
    {
        static $_config = [];
        if(empty($name)){
            $name = $this-&gt;getName();
        }
        if(isset($_config[$name])){
            return $_config[$name];
        }

        $map[&#039;name&#039;]    =   $name;
        $map[&#039;status&#039;]  =   1;
        $config  =   M(&#039;Addons&#039;)-&gt;where($map)-&gt;getField(&#039;config&#039;);
        if($config){
            $config   =   json_decode($config, true);
        }else{
            $config =   [];
            $temp_arr = include $this-&gt;config_file;
            foreach ($temp_arr as $key =&gt; $value) {
                if($value[&#039;type&#039;] == &#039;group&#039;){
                    foreach ($value[&#039;options&#039;] as $gkey =&gt; $gvalue) {
                        foreach ($gvalue[&#039;options&#039;] as $ikey =&gt; $ivalue) {
                            $config[$ikey] = $ivalue[&#039;value&#039;];
                        }
                    }
                }else{
                    $config[$key] = $temp_arr[$key][&#039;value&#039;];
                }
            }
        }
        $_config[$name]     =   $config;
        return $config;
    }

    /**
     * 必须实现安装
     * @return mixed
     */
    abstract public function install();

    /**
     * 必须卸载插件方法
     * @return mixed
     */
    abstract public function uninstall();
}2、在插件表(addons)，钩子表(hooks)中加入相应的数据INSERT INTO `addons` (`id`, `name`, `title`, `description`, `status`, `config`, `author`, `version`, `create_time`, `has_adminlist`) VALUES (&#039;2&#039;, &#039;test&#039;, &#039;test插件&#039;, &#039;test插件简介&#039;, &#039;1&#039;, NULL, &#039;byron sampson&#039;, &#039;0.1&#039;, &#039;1438154545&#039;, &#039;0&#039;);

INSERT INTO .`hooks` (`id`, `name`, `description`, `type`, `update_time`, `addons`, `status`) VALUES (&#039;21&#039;, &#039;demo&#039;, &#039;demo钩子&#039;, &#039;1&#039;, &#039;1384481614&#039;, &#039;test&#039;, &#039;1&#039;);3、在addons目录中创建一个test目录，且内部创建一个Test.php插件类&lt;?php
// +----------------------------------------------------------------------
// | test [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://www.zzstudio.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Byron Sampson &lt;xiaobo.sun@gzzstudio.net&gt;
// +----------------------------------------------------------------------
namespace addons\test;

class Test extends \addons\Base
{
    /**
     * 实现demo钩子
     * @param array $params
     */
    public function demo($params = [])
    {
        echo &#039;demo hook!page is &#039; . $params[&#039;p&#039;];
    }

    /**
     * 安装方法
     */
    public function install()
    {
        // TODO: Implement install() method.
    }

    /**
     * 卸载方法
     */
    public function uninstall()
    {
        // TODO: Implement uninstall() method.
    }
}4、在控制器中使用hook方法来调用钩子hook(&#039;demo&#039;, [&#039;p&#039;=&gt;&#039;page&#039;]);5、ok，大功告成来源：https://blog.zzstudio.net/bs/article_888.html</description>
</item>
</rdf:RDF>