sochishun

ThinkPHP 6.0 的环境要求如下:

  • PHP >= 7.1.0

安装 Composer

如果还没有安装 Composer,在 Linux 和 Mac OS X 中可以运行如下命令:

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

在 Windows 中,你需要下载并运行 Composer-Setup.exe。Composer 文档(英文文档中文文档)。

由于众所周知的原因,国外的网站连接速度很慢。建议使用国内镜像(阿里云)。

打开命令行窗口(windows用户)或控制台(Linux、Mac 用户)并执行如下命令:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

创建 ThinkPHP 项目

如果你是第一次安装的话,在命令行窗口执行命令:

composer create-project topthink/think my-thinkphp-app

已经安装过的项目可以执行下面的命令进行更新:

composer update

开启调试模式

应用默认是部署模式,在开发阶段,可以修改环境变量APP_DEBUG开启调试模式,上线部署后切换到部署模式。

重命名项目默认创建的 .example.env 文件为 .env

.env 文件内容:

APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = db_test
USERNAME = root
PASSWORD = 123456
HOSTPORT = 3306
CHARSET = utf8mb4
DEBUG = true

[LANG]
default_lang = zh-cn

运行项目

在命令行界面执行下面到指令

php think run

在浏览器中输入地址:

http://localhost:8000/

会看到欢迎页面。恭喜你,现在已经完成 ThinkPHP6.0 的安装!

如果你本地80端口没有被占用的话,也可以直接使用

php think run -p 80

然后就可以直接访问:

http://localhost/

安装常用插件

# 安装 ThinkPHP 官方视图插件
composer require topthink/think-view
# 安装 ThinkPHP 官方验证码插件
composer require topthink/think-captcha
# 安装 JWT Token 插件
composer require firebase/php-jwt
# 安装 邮件 插件
composer require swiftmailer/swiftmailer
# 安装 linux 定时任务 插件
composer require dragonmantank/cron-expression

设置上传目录

修改文件 config/filesystem.php

<?php

return [
    // 默认磁盘
    \'default\' => env(\'filesystem.driver\', \'local\'),
    // 磁盘列表
    \'disks\'   => [
        \'local\'  => [
            \'type\' => \'local\',
            \'root\' => app()->getRuntimePath() . \'storage\',
        ],
        \'public\' => [
            // 磁盘类型
            \'type\'       => \'local\',
            // 磁盘路径
            \'root\'       => app()->getRootPath() . \'public/storage\',
            // 磁盘路径对应的外部URL路径
            \'url\'        => \'/storage\',
            // 可见性
            \'visibility\' => \'public\',
        ],
        // 更多的磁盘配置信息

        // 网站上传目录,位置:public/uploads
        \'uploads\' => [
            // 磁盘类型
            \'type\'       => \'local\',
            // 磁盘路径
            \'root\'       => app()->getRootPath() . \'public/uploads\',
            // 磁盘路径对应的外部URL路径
            \'url\'        => \'/uploads\',
            // 可见性
            \'visibility\' => \'public\',
        ],
    ],
];

设置 Redis 缓存服务

修改文件 config/cache.php

<?php

// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------

return [
    // 默认缓存驱动
    \'default\' => env(\'cache.driver\', \'file\'),

    // 缓存连接方式配置
    \'stores\'  => [
        \'file\' => [
            // 驱动方式
            \'type\'       => \'File\',
            // 缓存保存目录
            \'path\'       => \'\',
            // 缓存前缀
            \'prefix\'     => \'\',
            // 缓存有效期 0表示永久缓存
            \'expire\'     => 0,
            // 缓存标签前缀
            \'tag_prefix\' => \'tag:\',
            // 序列化机制 例如 [\'serialize\', \'unserialize\']
            \'serialize\'  => [],
        ],
        // 更多的缓存连接

        // 应用数据(保存到 runtime/app_data 目录)
        \'app_data\' => [
            // 驱动方式
            \'type\'       => \'File\',
            // 缓存保存目录
            \'path\'       => app()->getRuntimePath() . \'app_data\',
            // 缓存前缀
            \'prefix\'     => \'\',
            // 缓存有效期 0表示永久缓存
            \'expire\'     => 0,
            // 缓存标签前缀
            \'tag_prefix\' => \'tag:\',
            // 序列化机制 例如 [\'serialize\', \'unserialize\']
            \'serialize\'  => [],
        ],

        // redis 缓存(若配置此选项,需开启 redis 服务,否则启动会报错)
        \'redis\'   =>  [
            // 驱动方式
            \'type\'   => \'redis\',
            // 服务器地址
            \'host\'       => \'127.0.0.1\',
        ],

        // session 缓存(使用 redis 保存 session 数据,需开启 redis 服务,否则启动会报错)
        \'session\'   =>  [
            // 驱动方式
            \'type\'   => \'redis\',
            // 服务器地址
            \'host\'       => \'127.0.0.1\',
            // 缓存前缀
            \'prefix\'     => \'sess_\',
        ],

    ],
];

修改 Session 存储方式

修改文件:/config/session.php

<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------

return [
    // session name
    \'name\'           => \'PHPSESSID\',
    // SESSION_ID的提交变量,解决flash上传跨域
    \'var_session_id\' => \'\',
    // 驱动方式 支持file cache
    // \'type\'           => \'file\',
    \'type\'           => \'cache\',
    // 存储连接标识 当type使用cache的时候有效
    // \'store\'          => null,
    \'store\'          => \'session\',
    // 过期时间
    // \'expire\'         => 1440,
    \'expire\'         => 86400, // 24 小时
    // 前缀
    \'prefix\'         => \'\',
];

修改路由配置文件,开启控制器后缀功能

修改文件:config/route.php

<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------

return [
    // pathinfo分隔符
    \'pathinfo_depr\'         => \'/\',
    // URL伪静态后缀
    \'url_html_suffix\'       => \'html\',
    // URL普通方式参数 用于自动生成
    \'url_common_param\'      => true,
    // 是否开启路由延迟解析
    \'url_lazy_route\'        => false,
    // 是否强制使用路由
    // \'url_route_must\'        => false,
    \'url_route_must\'        => true,
    // 合并路由规则
    \'route_rule_merge\'      => false,
    // 路由是否完全匹配
    \'route_complete_match\'  => false,
    // 访问控制器层名称
    \'controller_layer\'      => \'controller\',
    // 空控制器名
    \'empty_controller\'      => \'Error\',
    // 是否使用控制器后缀(若设为 true 值,则控制器需加后缀 Controller,例如:UserController.php)
    \'controller_suffix\'     => true,
    // 默认的路由变量规则
    \'default_route_pattern\' => \'[\w\.]+\',
    // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
    \'request_cache_key\'     => false,
    // 请求缓存有效期
    \'request_cache_expire\'  => null,
    // 全局请求缓存排除规则
    \'request_cache_except\'  => [],
    // 默认控制器名
    \'default_controller\'    => \'Index\',
    // 默认操作名
    \'default_action\'        => \'index\',
    // 操作方法后缀
    \'action_suffix\'         => \'\',
    // 默认JSONP格式返回的处理方法
    \'default_jsonp_handler\' => \'jsonpReturn\',
    // 默认JSONP处理方法
    \'var_jsonp_handler\'     => \'callback\',
];

设置验证码

修改配置文件:config/captcha.php

<?php
// +----------------------------------------------------------------------
// | Captcha配置文件
// +----------------------------------------------------------------------

return [
    //验证码位数
    \'length\'   => 4,
    // 验证码字符集合
    \'codeSet\'  => \'2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY\',
    // 验证码过期时间
    \'expire\'   => 1800,
    // 是否使用中文验证码
    \'useZh\'    => false, 
    // 是否使用算术验证码
    \'math\'     => false,
    // 是否使用背景图
    \'useImgBg\' => false,
    //验证码字符大小
    // \'fontSize\' => 25,
    \'fontSize\' => 30,
    // 是否使用混淆曲线
    \'useCurve\' => false,
    //是否添加杂点
    \'useNoise\' => false,
    // 验证码字体 不设置则随机
    \'fontttf\'  => \'\',
    //背景颜色
    \'bg\'       => [243, 251, 254],
    // 验证码图片高度
    \'imageH\'   => 0,
    // 验证码图片宽度
    \'imageW\'   => 0,

    // 添加额外的验证码设置
    // verify => [
    //     \'length\'=>4,
    //    ...
    //],
];

开启 Session 中间件

文件:app/middleware.php

<?php
// 全局中间件定义文件
return [
    // 全局请求缓存
    // \think\middleware\CheckRequestCache::class,
    // 多语言加载
    // \think\middleware\LoadLangPack::class,
    // Session初始化
    \think\middleware\SessionInit::class
];

通用助手类

<?php
/**
 * 通用助手类
 */
declare(strict_types=1);

namespace app\helper;

use think\facade\Cache;

/**
 * 通用 助手类
 */
class CommonHelper
{
    /**
     * 获取标准消息格式
     * @param integer|boolean $status
     * @param string $msg
     * @param mixed $data
     * @param integer $code
     * @return array [\'status\',\'msg\',\'data\',\'code\']
     */
    public static function stdmessage($status, $msg, $data = \'\', $code = 0)
    {
        return [
            \'status\' => intval($status),
            \'msg\'  => $msg,
            \'data\' => $data,
            \'code\' => $code,
        ];
    }

    /**
     * 生成参数签名
     * @param array &$params 请求参数数组
     * @param integer $appid 应用ID
     * @param string $appkey 应用KEY
     * @return string 返回sign参数签名字符串
     */
    public static function makeParamSignature(&$params, $appid, $appkey)
    {
        // 过滤空数组
        $params = array_filter($params);
        // 加入时间戳参数
        $params[\'timestamp\'] = time();
        // 加入应用ID参数
        $params[\'appid\'] = $appid;
        // 加入应用Key参数
        $params[\'appkey\'] = $appkey;
        // 加入随机值参数
        $params[\'nonce\'] = substr(uniqid(), 7);
        // 数组按键值正序重新排序
        ksort($params);
        // 用md5加密重新串联成请求字符串的参数数组
        $sign = md5(http_build_query($params));
        // 截取中间16位作为签名
        $sign = substr($sign, 8, 16);
        // 删除appkey参数
        unset($params[\'appkey\']);
        // 加入签名参数
        $params[\'sign\'] = $sign;
        return $sign;
    }
    /**
     * 验证参数签名
     * @param array $params 请求参数数组,一般为 appid,nonce,sign,timestamp 四个就可以了
     * @return array 
     */
    public static function validateParamSignature($params)
    {
        if (!is_array($params)) {
            return [\'status\' => false, \'message\' => \'签名校验失败:请求参数错误\'];
        }
        $needKeys = [\'timestamp\', \'appid\', \'sign\', \'nonce\'];
        foreach ($needKeys as $key) {
            if (empty($params[$key])) {
                return [\'status\' => false, \'message\' => \'签名校验失败:请求参数无效\'];
            }
        }
        array_filter($params);
        extract($params);
        // 链接请求1分钟内使用有效
        $invideTimeStamp = time() - 360;
        if ($timestamp < $invideTimeStamp) {
            return [\'status\' => false, \'message\' => \'签名校验失败:请求过期失效\'];
        }
        if ($appid == \'-1\') {
            $appkey = \'a99AE2d2a736X65f5Ye63Ae299b0e339\';
        } else {
            $appkey = Cache::get(\'appid:\' . $appid); // 获取appkey
        }
        if (!$appkey) {
            return [\'status\' => false, \'message\' => \'签名校验失败:应用未注册\'];
        }
        unset($params[\'sign\']);
        $params[\'appkey\'] = $appkey;
        ksort($params);
        $servSign = substr(md5(http_build_query($params)), 8, 16);
        if ($sign != $servSign) {
            return [\'status\' => false, \'message\' => \'签名校验失败:签名无效 \' . $servSign];
        }
        return [\'status\' => true, \'message\' => \'签名校验成功:签名有效\'];
    }
    /**
     * 密码加密
     */
    public static function hashPassword($password, $salt = \'\')
    {
        // 截取中间16位作为签名
        return substr(md5($password . $salt), 8, 16);
    }

    /**
     * 获取不重复序列号
     * 大约是原来长度的一半,比如12位生成6位,21位生成13位
     */
    public static function hashSerial($prefix = \'\')
    {
        $time = date(\'y-m-d-h-i-s\');
        if (is_numeric($prefix)) {
            $time = chunk_split(strval($prefix), 2, \'-\') . $time;
            $prefix = \'\';
        }
        $atime = explode(\'-\', $time);
        foreach ($atime as $stime) {
            $itime = $stime * 1;
            if ($itime < 26) {
                $prefix .= chr(65 + $itime);
                continue;
            }
            if ($itime >= 48 && $itime <= 57) {
                $prefix .= chr($stime);
                continue;
            }
            $prefix .= $stime;
        }
        return $prefix;
    }

    /**
     * 语义化时间
     * 
     * @param integer|string $time 时间
     * @param string $break 断点,超过断点以后的时间会直接以指定的日期格式显示
     * @param string $format 日期格式, 与$break参数结合使用
     * @param boolean $aliasable 是否允许以 昨天、前天 来代替 1 天前、2 天前
     * @return string 返回语义化时间,例如:几秒,几分,几小时,几天前,几小时前,几月前 等
     * @example 
     *      humantime(strtotime(\'-5 month\'), \'month\') 返回 2019-10-27 17:50:17
     *      humantime(strtotime(\'-5 month\'), \'year\') 返回 5 个月前
     *      humantime(strtotime(\'yesterday\')) 返回 昨天
     *      humantime(strtotime(\'-2 day\')); 返回 前天
     */
    public static function humantime($time, $break = \'\', $format = \'Y-m-d H:i:s\', $aliasable = true)
    {
        if (!$time) {
            return \'\';
        }
        if (!is_numeric($time)) {
            $time = strtotime($time);
        }
        $text = \'\';
        $seconds = time() - $time;
        if ($seconds > 0) {
            $formater = array(
                \'second\' => [\'time\' => \'1\', \'text\' => \'秒\'],
                \'minute\' => [\'time\' => \'60\', \'text\' => \'分钟\'],
                \'hour\' => [\'time\' => \'3600\', \'text\' => \'小时\'],
                \'day\' => [\'time\' => \'86400\', \'text\' => \'天\', \'alias\' => [\'1\' => \'昨天\', \'2\' => \'前天\']],
                \'week\' => [\'time\' => \'604800\', \'text\' => \'星期\'],
                \'month\' => [\'time\' => \'2592000\', \'text\' => \'个月\'],
                \'year\' => [\'time\' => \'31536000\', \'text\' => \'年\'],
            );
            $prevName = \'\';
            foreach ($formater as $name => $data) {
                if ($seconds < intval($data[\'time\'])) {
                    $prevData = $formater[$prevName];
                    $count = floor($seconds / intval($prevData[\'time\']));
                    if ($aliasable && isset($prevData[\'alias\']) && isset($prevData[\'alias\'][strval($count)])) {
                        $text = $prevData[\'alias\'][strval($count)];
                        break;
                    }
                    $text = $count . \' \' . $prevData[\'text\'] . \'前\';
                    break;
                }
                $prevName = $name;
                if ($break && ($name == $break)) {
                    $text = date($format, $time);
                    break;
                }
            }
        } else {
            $text = date($format, $time);
        }
        return $text;
    }

    /**
     * 解析字符串类型的 ID 值
     * @param integer|string $id 以逗号隔开的编号值,例如:1,3,5
     * @param string $separator 分割符号,默认是逗号
     * @return integer|array 返回安全的数值
     */
    public static function parseTextIds($id, $separator = \',\')
    {
        if (is_numeric($id)) {
            return $id;
        }
        $ids = [];
        $data = explode($separator, $id);
        foreach ($data as $v) {
            if (is_numeric($v)) {
                $ids[] = intval($v);
            }
        }
        return array_filter($ids);
    }

    /**
     * 返回当前的毫秒时间戳
     */
    public static function microtime()
    {
        return round(microtime(true) * 1000);
    }

    /**
     * 字符串转二维数组
     * 说明:如果是url请求字符串,可以通过原生方法 parse_str 和 http_build_query 来互相转换
     * @param string $text 文本内容
     * @param string $groupSeparator 组分隔符
     * @param string $valueSeparator 值分隔符
     * @return array 键值数组
     * 示例:text2array(\'a=1;b=2\',\';\',\'=\')
     */
    public static function text2array($text, $groupSeparator = "\n", $valueSeparator = \'=\')
    {
        $text = trim($text);
        $data = [];
        if (!$text) {
            return $data;
        }
        $arr = array_filter(explode($groupSeparator, $text));
        foreach ($arr as $row) {
            $pair = explode($valueSeparator, $row, 2);
            $data[trim($pair[0])] = trim($pair[1]);
        }
        return $data;
    }

    /**
     * ver_export() 方法的现代风格版
     */
    function varExport($var, $indent = "")
    {
        switch (gettype($var)) {
            case "string":
                return \'\\'\' . addcslashes($var, "\\\$\"\r\n\t\v\f") . \'\\'\';
            case "array":
                $indexed = array_keys($var) === range(0, count($var) - 1);
                $r = [];
                foreach ($var as $key => $value) {
                    $r[] = "$indent    " . ($indexed ? "" : $this->varExport($key) . " => ") . $this->varExport($value, "$indent    ");
                }
                return "[\n" . implode(",\n", $r) . "\n" . $indent . "]";
            case "boolean":
                return $var ? "TRUE" : "FALSE";
            default:
                return var_export($var, true);
        }
    }
}

JWT Token 插件的使用示例

<?php
/**
 * JWT Token 助手类
 */
declare(strict_types=1);

namespace app\helper;

use \Firebase\JWT\JWT;

/**
 * JSON Web Tokens 助手类
 * https://jwt.io/
 * https://github.com/firebase/php-jwt
 * composer require firebase/php-jwt
 */
class JWTHelper
{
    /**
     * 加密
     * @param string $iss jwt 签发者(网址或IP,例如:http://example.com)
     * @param string $aud 接收 jwt 的一方(网址或IP,例如:http://example.com)
     * @param integer $nbf 定义在什么时间之前,该 jwt 都是不可用的.(例如:strtotime(\'10 hours\'))
     * @param array $extConf 扩展参数
     * @param string $key 密钥
     * @return string jwt-token 内容
     * 使用方法:JWTHelper::encode(\'\', \'\', 0, [\'user_id\' => 1]);
     */
    public static function encode($iss, $aud, $nbf, $extConf = [], $key = \'my-jwt-key\')
    {
        if (!$nbf) {
            $nbf = strtotime(\'10 hours\');
        }
        // 载荷(存放有效信息的地方)
        $payload = array(
            // iss: jwt签发者(网址或IP,例如:http://example.com)
            "iss" => $iss ?: $iss, request()->domain(),
            // aud: 接收jwt的一方(网址或IP,例如:http://example.com)
            "aud" => $aud,
            // iat: jwt的签发时间
            "iat" => time(),
            // nbf: 定义在什么时间之前,该jwt都是不可用的.(例如:strtotime(\'10 hours\'))
            "nbf" => $nbf,
        );
        if (!empty($extConf)) {
            $payload = array_merge($payload, $extConf);
        }

        /**
         * IMPORTANT:
         * You must specify supported algorithms for your application. See
         * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
         * for a list of spec-compliant algorithms.
         */
        return JWT::encode($payload, $key);
    }

    /**
     * 解密
     * @param string $jwt jwt-token 字符串
     * @param string $key 秘钥
     * @return array 返回 payload 数组内容
     * 使用方法:
     *  try {
     *      $tokenInfo = (array) JWTHelper::decode($token);
     *      $userId = intval($tokenInfo[\'user_id\']);
     *  } catch (\Exception $ex) {
     *      return $ex;
     *  }
     */
    public static function decode($jwt, $key = \'my-jwt-key\')
    {
        JWT::$leeway = 36000; // 延迟10小时
        try {
            return JWT::decode($jwt, $key, array(\'HS256\'));
        } catch (\Exception $ex) {
            throw $ex;
        }
    }
}

Excel 插件的使用示例

<?php
/**
 * Excel 文档数据处理助手类
 */
declare(strict_types=1);

namespace app\helper;

use InvalidArgumentException;
use PhpOffice\PhpSpreadsheet\IOFactory;

/**
 * Excel 助手类
 */
class ExcelHelper
{

    /**
     * 导入excel文件
     * @param  string $filename excel文件路径
     * @return array excel文件内容数组
     */
    public static function importExcel($filename)
    {
        if ($filename) {
            $filename = \'.\' . $filename;
        }
        if (!$filename || !file_exists($filename)) {
            return CommonHelper::stdmessage(0, \'文件不存在! \' . $filename);
        }
        // 判断文件是什么格式
        $fileExt = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        if (!in_array($fileExt, [\'csv\', \'xls\', \'xlsx\'])) {
            return CommonHelper::stdmessage(0, \'文件格式错误, 只支持csv,xls,xlsx格式的文件!\');
        }
        ini_set(\'max_execution_time\', \'0\');
        try {            
            $spreadsheet = IOFactory::load($filename);
            return CommonHelper::stdmessage(1, \'\', $spreadsheet->getActiveSheet()->toArray(null, true, true, true));
        } catch (InvalidArgumentException $e) {
            return CommonHelper::stdmessage(0, $e->getMessage()); 
        }
    }
}

Http 模拟请求助手类

<?php
/**
 * HTTP 模拟请求助手类
 */

declare(strict_types=1);

namespace app\helper;

/**
 * Http 模拟请求助手类
 */
class HttpHelper
{
    /**
     * Ping IP 是否可用
     * 依赖:需要开启扩展 extension=sockets
     */
    public static function ping($ip, $port = 80)
    {
        if (strpos($ip, \':\')) {
            list($ip, $port) = explode(\':\', $ip);
            $port = intval($port);
        }
        $socket = null;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
        } else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        } else {
            return false;
        }
        return socket_connect($socket, $ip, $port);
    }

    /**
     * 发送一个POST请求
     * @param string $url     请求URL(带Http的完整地址)
     * @param array  $params  请求参数
     * @param array  $options 扩展参数
     * @return mixed|string
     */
    public static function post($url, $params = [], $options = [])
    {
        $req = self::sendRequest($url, $params, \'POST\', $options);
        return $req;
    }

    /**
     * 发送一个GET请求
     * @param string $url     请求URL
     * @param array  $params  请求参数
     * @param array  $options 扩展参数
     * @return mixed|string
     */
    public static function get($url, $params = [], $options = [])
    {
        $req = self::sendRequest($url, $params, \'GET\', $options);
        return $req;
    }

    /**
     * CURL发送Request请求,含POST和REQUEST
     * @param string $url     请求的链接(带Http的完整地址)
     * @param mixed  $params  传递的参数
     * @param string $method  请求的方法
     * @param mixed  $options CURL的参数
     * @return array
     */
    public static function sendRequest($url, $params = [], $method = \'POST\', $options = [])
    {
        $msgInfo = [
            \'status\' => 0,
            \'msg\'   => \'\',
            \'code\' => 0,
            \'data\'  => [],
        ];
        if (!$url || 0 !== strpos($url, \'http\')) {
            $msgInfo[\'msg\'] = \'URL地址无效:\' . $url;
            return $msgInfo;
        }
        $method = strtoupper($method);
        $protocol = substr($url, 0, 5);
        $query_string = is_array($params) ? http_build_query($params) : $params;

        $ch = curl_init();
        $defaults = [];
        if (\'GET\' == $method) {
            $geturl = $query_string ? $url . (stripos($url, "?") !== false ? "&" : "?") . $query_string : $url;
            $defaults[CURLOPT_URL] = $geturl;
        } else {
            $defaults[CURLOPT_URL] = $url;
            if ($method == \'POST\') {
                $defaults[CURLOPT_POST] = 1;
            } else {
                $defaults[CURLOPT_CUSTOMREQUEST] = $method;
            }
            $defaults[CURLOPT_POSTFIELDS] = $params;
        }

        $defaults[CURLOPT_HEADER] = false;
        $defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
        $defaults[CURLOPT_FOLLOWLOCATION] = true;
        $defaults[CURLOPT_RETURNTRANSFER] = true;
        $defaults[CURLOPT_CONNECTTIMEOUT] = 3;
        $defaults[CURLOPT_TIMEOUT] = 3;

        // disable 100-continue
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(\'Expect:\'));

        if (\'https\' == $protocol) {
            $defaults[CURLOPT_SSL_VERIFYPEER] = false;
            $defaults[CURLOPT_SSL_VERIFYHOST] = false;
        }

        curl_setopt_array($ch, (array)$options + $defaults);

        $ret = curl_exec($ch);
        $err = curl_error($ch);

        if (false === $ret || !empty($err)) {
            $errno = curl_errno($ch);
            $info = curl_getinfo($ch);
            curl_close($ch);
            $msgInfo[\'msg\'] = $err;
            $msgInfo[\'code\'] = $errno;
            $msgInfo[\'data\'] = $info;
            return $msgInfo;
        }
        curl_close($ch);
        $msgInfo[\'status\'] = 1;
        $msgInfo[\'data\'] = $ret;
        return $msgInfo;
    }

    /**
     * 异步发送一个请求
     * @param string $url    请求的链接
     * @param mixed  $params 请求的参数
     * @param string $method 请求的方法
     * @return boolean TRUE
     */
    public static function sendAsyncRequest($url, $params = [], $method = \'POST\')
    {
        $method = strtoupper($method);
        $method = $method == \'POST\' ? \'POST\' : \'GET\';
        //构造传递的参数
        if (is_array($params)) {
            $post_params = [];
            foreach ($params as $k => &$v) {
                if (is_array($v)) {
                    $v = implode(\',\', $v);
                }
                $post_params[] = $k . \'=\' . urlencode($v);
            }
            $post_string = implode(\'&\', $post_params);
        } else {
            $post_string = $params;
        }
        $parts = parse_url($url);
        //构造查询的参数
        if ($method == \'GET\' && $post_string) {
            $parts[\'query\'] = isset($parts[\'query\']) ? $parts[\'query\'] . \'&\' . $post_string : $post_string;
            $post_string = \'\';
        }
        $parts[\'query\'] = isset($parts[\'query\']) && $parts[\'query\'] ? \'?\' . $parts[\'query\'] : \'\';
        //发送socket请求,获得连接句柄
        $fp = fsockopen($parts[\'host\'], isset($parts[\'port\']) ? $parts[\'port\'] : 80, $errno, $errstr, 3);
        if (!$fp) {
            return false;
        }
        //设置超时时间
        stream_set_timeout($fp, 3);
        $out = "{$method} {$parts[\'path\']}{$parts[\'query\']} HTTP/1.1\r\n";
        $out .= "Host: {$parts[\'host\']}\r\n";
        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $out .= "Content-Length: " . strlen($post_string) . "\r\n";
        $out .= "Connection: Close\r\n\r\n";
        if ($post_string !== \'\') {
            $out .= $post_string;
        }
        fwrite($fp, $out);
        //不用关心服务器返回结果
        //echo fread($fp, 1024);
        fclose($fp);
        return true;
    }

    /**
     * 发送文件到客户端
     * @param string $file
     * @param bool   $delaftersend
     * @param bool   $exitaftersend
     */
    public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
    {
        if (file_exists($file) && is_readable($file)) {
            header(\'Content-Description: File Transfer\');
            header(\'Content-Type: application/octet-stream\');
            header(\'Content-Disposition: attachment;filename = \' . basename($file));
            header(\'Content-Transfer-Encoding: binary\');
            header(\'Expires: 0\');
            header(\'Cache-Control: must-revalidate, post-check = 0, pre-check = 0\');
            header(\'Pragma: public\');
            header(\'Content-Length: \' . filesize($file));
            ob_clean();
            flush();
            readfile($file);
            if ($delaftersend) {
                unlink($file);
            }
            if ($exitaftersend) {
                exit;
            }
        }
    }
}

邮件 插件的使用示例

<?php
/**
 * 邮件 助手类
 */
declare(strict_types=1);

namespace app\helper;

use Swift_Mailer;
use Swift_Message;
use Swift_SmtpTransport;
use think\facade\Config;

/**
 * 邮件 助手类
 */
class MailHelper
{
    protected $user = \'\';
    protected $password = \'\';
    protected $host = \'\';
    protected $port = 25;
    protected $fromEmail = [];

    public function initByConfig()
    {
        $smtpInfo=Config::get(\'site.email.smtp\');
        if(!isset($smtpInfo[\'password\'])){
            return;
        }
        $this->user = $smtpInfo[\'username\'];
        $this->password = $smtpInfo[\'password\'];
        $this->host = $smtpInfo[\'host\'];
        $this->port = $smtpInfo[\'port\'];
    }
    public function setSmtp($user, $password, $host, $port = 25)
    {
        $this->user = $user;
        $this->password = $password;
        $this->host = $host;
        $this->port = $port;
    }
    public function send($subject, $body, $toEmail, $fromEmail=\'\')
    {
        if ($fromEmail) {
            $this->fromEmail = $fromEmail;
        } else {
            $fromEmail = $this->fromEmail;
        }
        if (!$fromEmail || !$toEmail || !$subject) {
            return CommonHelper::stdmessage(0, \'参数无效\');
        }
        // Create the Transport
        $transport = (new Swift_SmtpTransport($this->host, $this->port))
            ->setUsername($this->user)
            ->setPassword($this->password);

        // Create the Mailer using your created Transport
        $mailer = new Swift_Mailer($transport);

        // Create a message
        $message = (new Swift_Message($subject))
            ->setFrom($fromEmail)
            ->setTo($toEmail)
            ->setBody($body);

        // Send the message
        $result = $mailer->send($message);
        return CommonHelper::stdmessage($result, $result ? \'\' : \'发送失败\');
    }
}

图片处理助手类

<?php
/**
 * 图片处理 助手类
 */

declare(strict_types=1);

namespace app\helper;

/**
 * 图片处理助手类
 */
class ImageHelper
{
    // 常用文件大小字节常量
    const SIZE_50KB = 51200;
    const SIZE_200KB = 204800;
    const SIZE_500KB = 512000;
    const SIZE_1MB = 1048576;
    const SIZE_2MB = 2097152;

    const IMAGE_MIME = [\'image/gif\', \'image/jpg\', \'image/jpeg\', \'image/bmp\', \'image/png\', \'image/webp\'];
    const IMAGE_EXT = [\'gif\', \'jpg\', \'jpeg\', \'bmp\', \'png\', \'webp\'];

    /**
     * 获取图像类型
     * 返回图像常量值(1=gif,2=jpeg,3=png,6=bmp),否则返回 false。
     */
    public static function getImageType($fileName)
    {
        if (function_exists(\'exif_imagetype\')) {
            // 返回对应的常量,否则返回 FALSE。
            // 详见:https://www.php.net/manual/zh/function.exif-imagetype.php
            return exif_imagetype($fileName);
        }

        try {
            // 获取图像大小及相关信息,成功返回一个数组(宽度、高度、类型常量、宽高属性、颜色位数、通道值、 MIME信息),失败则返回 FALSE 
            // 详见:https://www.php.net/manual/zh/function.getimagesize.php
            // 警告:getimagesize存在上传漏洞。需要额外的条件检查,比如文件大小、扩展名、文件类型,并设置上传目录不允许执行PHP文件。
            $info = getimagesize($fileName);
            return $info ? $info[2] : false;
        } catch (\Exception $e) {
            return false;
        }
    }
    public static function checkImageMime($mimeType)
    {
        return in_array($mimeType, self::IMAGE_MIME);
    }
    public static function checkImageExt($ext)
    {
        return in_array($ext, self::IMAGE_EXT);
    }
    public static function checkWidthAndHeight($fileName, $imgWidth, $imgHeight)
    {
        try {
            // 获取图像大小及相关信息,成功返回一个数组(宽度、高度、类型常量、宽高属性、颜色位数、通道值、 MIME信息),失败则返回 FALSE 
            // 详见:https://www.php.net/manual/zh/function.getimagesize.php
            // 警告:getimagesize存在上传漏洞。需要额外的条件检查,比如文件大小、扩展名、文件类型,并设置上传目录不允许执行PHP文件。
            $imgInfo = getimagesize($fileName);
            if (!$imgInfo || !isset($imgInfo[2])) {
                return \'文件不是有效的图像\';
            }
            if (($imgWidth && $imgWidth != $imgInfo[0]) || ($imgHeight && $imgHeight != $imgInfo[1])) {
                return "图片尺寸 [宽度{$imgInfo[0]}, 高度{$imgInfo[1]}] 不符合规范 [宽度{$imgWidth}, 高度{$imgHeight}]";
            }
            return true;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
}

文件上传控制器类

<?php
/**
 * 文件上传 控制器类
 */
declare(strict_types=1);

namespace app\controller\admin;

use think\Request;
use app\helper\CommonHelper;
use app\helper\ImageHelper;

/**
 * 文件上传控制器
 */
class UploaderController
{
    /**
     * 保存上传的文件(根据is_multiple参数自动识别多文件和单文件上传)
     */
    public function save(Request $request)
    {
        // 禁止上传 PHP 和 HTML 文件
        $forbiddenMimes = [\'text/x-php\', \'text/html\', \'text/css\', \'text/javascript\', \'text/x-shellscript\', \'application/x-javascript\'];
        $forbiddenExts = [\'php\', \'html\', \'htm\', \'js\', \'css\'];
        // 获取表单参数
        $imgWidth = $request->param(\'imgwidth/d\', 0);
        $imgHeight = $request->param(\'imgheight/d\', 0);
        $isMultipleFile = $request->param(\'is_multiple/d\', 0);
        $allfiles = $request->file();
        // 单文件:[\'file\'=>[\'originalName\'=>\'xxx.png\', \'mimeType\'=>\'image/png\', \'error\'=>0, ...]]
        // 多文件:[\'file\'=>[0=>[\'originalName\'=>\'xxx.png\', \'mimeType\'=>\'image/png\', \'error\'=>0, ...]]]
        // 如果是单文件,先封装成多文件数据格式
        $firstFileKey = key($allfiles);
        if (gettype(current($allfiles)) == \'object\') {
            $allfiles[$firstFileKey] = [$allfiles[$firstFileKey]];
        }
        // 自动识别是否多文件上传
        if (!$isMultipleFile && count($allfiles) > 1) {
            $isMultipleFile = 1;
        }
        $returnInfo = [];
        foreach ($allfiles as $files) {
            foreach ($files as $file) {
                $fileInfo = [
                    \'mime\' => $file->getMime(),
                    \'ext\' => $file->getExtension(),
                    \'file_name\' => $file->getOriginalName(),
                    \'size\' => $file->getSize(), //文件大小,单位字节
                    \'tmp_name\' => $file->getPathname(), // 全路径
                ];
                // 文件大小校验(2MB)
                if ($fileInfo[\'size\'] > 2097152) {
                    $returnInfo[] = CommonHelper::stdmessage(0, \'文件大小超过最大上传限制\', $fileInfo[\'file_name\']);
                    continue;
                }
                if (in_array($fileInfo[\'mime\'], $forbiddenMimes) || in_array($fileInfo[\'ext\'], $forbiddenExts)) {
                    $returnInfo[] = CommonHelper::stdmessage(0, \'文件类型不允许上传\', $fileInfo[\'file_name\']);
                    continue;
                }
                //验证是否为图片文件
                if (ImageHelper::checkImageMime($fileInfo[\'mime\'])) {
                    $message = ImageHelper::checkWidthAndHeight($fileInfo[\'tmp_name\'], $imgWidth, $imgHeight);
                    if (true !== $message) {
                        $returnInfo[] = CommonHelper::stdmessage(0, $message, $fileInfo[\'file_name\']);
                        continue;
                    }
                }
                // 上传到本地服务器(\'https://picsum.photos/200/300\')
                $saveName = \think\facade\Filesystem::disk(\'uploads\')->putFile(date(\'Y\'), $file);
                
                $fileName = \'/uploads/\' . str_replace(\'\\\', \'/\', $saveName);
                $returnInfo[] = CommonHelper::stdmessage(1, $fileInfo[\'file_name\'], $fileName);
            }
        }
        if ($isMultipleFile) {
            return json(CommonHelper::stdmessage(1, \'\', $returnInfo));
        } else {
            return json(current($returnInfo));
        }
    }

    /**
     * 删除指定资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function delete(Request $request)
    {
        $filePath = $request->param(\'filePath\');
        return json(CommonHelper::stdmessage(1, \'\', [\'file\' => $filePath]));
    }

    /**
     * PHP 原生上传处理
     */
    public function orgupload()
    {
        $ofile = $_FILES[\'file\'];
        if (!$ofile) {
            output_json(stdmessage(0, \'未选择任何文件\'));
        }
        if ($ofile["error"] > 0) {
            output_json(stdmessage(0, $ofile["error"]));
        }
        $fileInfo = [
            // 上传文件名
            \'file_name\' => $ofile["name"],
            // 文件类型
            \'file_type\' => $ofile["type"],
            // 文件大小
            \'file_size\' => $ofile["size"],
            // 文件临时存储的位置
            \'tmp_name\' => $ofile["tmp_name"],
            // 扩展名
            \'file_ext\' => pathinfo($ofile[\'name\'], PATHINFO_EXTENSION),
        ];
        // 验证文件后缀
        if (!in_array($fileInfo[\'file_ext\'], ["gif", "jpeg", "jpg", "png"])) {
            output_json(stdmessage(0, \'不是有效的图形文件\', $fileInfo[\'file_ext\']));
        }
        // 验证文件类型
        if (0 !== strpos($fileInfo[\'file_type\'], \'image/\')) {
            output_json(stdmessage(0, \'不是有效的图形文件\', $fileInfo[\'file_type\']));
        }
        // 验证文件大小
        $maxFileSize = 1024 * 1024 * 2;
        if ($fileInfo[\'file_size\'] > $maxFileSize) {
            output_json(stdmessage(0, \'文件尺寸超过 2 MB\'));
        }

        $dir = \'./files/\';
        $saveFileName = $dir . md5($fileInfo[\'name\']) . \'.\' . $fileInfo[\'file_ext\'];
        // 尝试自动创建目录
        if (!is_dir($dir) && !@mkdir($dir, 0777)) {
            output_json(stdmessage(0, \'创建目录失败\'));
        }

        // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
        move_uploaded_file($ofile["tmp_name"], $saveFileName);
        output_json(stdmessage(1, \'\', $saveFileName));

        // ===============通用函数库=============

        function stdmessage($code, $msg, $data = \'\')
        {
            return [\'code\' => $code, \'msg\' => $msg, \'data\' => $data];
        }
        function output_json($msgInfo)
        {
            var_export($msgInfo);
            exit;
            header(\'Content-Type:application/json; charset=utf-8\');
            echo json_encode($msgInfo);
            exit;
        }
    }
}

管理员控制器代码示例

<?php

declare(strict_types=1);

namespace app\controller\admin;

use think\Request;
use think\facade\View;
use app\model\pedm_auth\AdminModel;
use app\helper\CommonHelper;
use app\helper\StringHelper;

/**
 * 管理员 控制器 
 *
 */
class AdminController extends BaseController
{
    /**
     * 显示资源列表页
     */
    public function index()
    {
        return View::fetch();
    }

    /**
     * 显示创建资源表单页.
     */
    public function create()
    {
        return View::fetch(\'\');
    }

    /**
     * 显示指定的资源
     */
    public function profile()
    {
        return View::fetch(\'\');
    }

    /**
     * 显示编辑资源表单页.
     */
    public function edit()
    {
        return View::fetch(\'\');
    }

    /**
     * 获取资源列表
     */
    public function list(Request $request)
    {
        $searchText = $request->param(\'search_text\');
        $pageSize = $this->getPageSize();
        if ($searchText) {
            $list = AdminModel::where(\'id\', intval($searchText))->paginate($pageSize);
        } else {
            $list = AdminModel::paginate($pageSize);
        }
        return json(CommonHelper::stdmessage(1, \'\', $list->append([\'status_text\', \'sex_text\'])));
    }

    /**
     * 获取一条资源
     */
    public function read(Request $request)
    {
        $id = $request->param(\'id/d\', 0);
        if ($id > 0) {
            $model = AdminModel::find($id);
        } else {
            $model = new AdminModel();
            $model->sex = 0;
            $model->status = 1;
        }
        $viewData = [
            \'data\' => $model,
            \'sex_data\' => $model->getSexData(),
            \'status_data\' => $model->getStatusData(),
        ];
        return json($viewData);
    }

    /**
     * 保存新建的资源
     */
    public function save(Request $request)
    {
        if (!$request->isPost()) {
            return json(CommonHelper::stdmessage(0, \'非法请求\'));
        }
        // 表单校验(alphaDash: 字母和数字,下划线_及破折号-)
        $validate = \think\facade\Validate::rule([
            \'user_name|用户名\' => \'require|max:60\',
            \'password|密码\' => \'require|max:32\',
            \'avatar|头像\' => \'max:200\',
            \'email|邮件\' => \'email|max:100\',
            \'mobile|手机号\' => \'max:11\',
        ]);
        $postData = $request->param();

        if (!$validate->check($postData)) {
            return json(CommonHelper::stdmessage(0, $validate->getError()));
        }
        // 表单数据补全
        $postData[\'register_ip\'] = $request->ip();
        if (isset($postData[\'password\'])) {
            $postData[\'password\']=trim($postData[\'password\']);
            if ($postData[\'password\'] == \'\') {
                unset($postData[\'password\']);
            } else {
                $postData[\'salt\'] = StringHelper::newAlpha(6);
                $postData[\'password\'] = CommonHelper::hashPassword($postData[\'password\'], $postData[\'salt\']);
            }
        }
        // 保存到数据库
        $resultInfo = [];
        try {
            AdminModel::create($postData);
            $resultInfo = CommonHelper::stdmessage(1, \'\');
        } catch (\Exception $e) {
            // 数据库操作失败 输出错误信息
            $resultInfo = CommonHelper::stdmessage(0, $e->getMessage());
        }
        return json($resultInfo);
    }

    /**
     * 保存更新的资源
     */
    public function update(Request $request)
    {
        if (!$request->isPost()) {
            return json(CommonHelper::stdmessage(0, \'非法请求\'));
        }
        // 表单校验(alphaDash: 字母和数字,下划线_及破折号-)
        $validate = \think\facade\Validate::rule([
            \'id\' => \'require|number\',
            \'nick_name|昵称\' => \'max:60\',
            \'avatar|头像\' => \'max:200\',
            \'email|邮件\' => \'email|max:100\',
            \'mobile|手机号\' => \'max:11\',
        ]);
        $postData = $request->param();

        if (!$validate->check($postData)) {
            return json(CommonHelper::stdmessage(0, $validate->getError()));
        }
        // 表单数据处理
        if (isset($postData[\'password\'])) {
            $postData[\'password\']=trim($postData[\'password\']);
            if ($postData[\'password\'] == \'\') {
                unset($postData[\'password\']);
            } else {
                $postData[\'salt\'] = StringHelper::newAlpha(6);
                $postData[\'password\'] = CommonHelper::hashPassword($postData[\'password\'], $postData[\'salt\']);
            }
        }
        $id = $postData[\'id\'];
        unset($postData[\'id\']);
        // 保存到数据库
        $resultInfo = [];
        try {
            AdminModel::where(\'id\', $id)->update($postData);
            $resultInfo = CommonHelper::stdmessage(1, \'\');
        } catch (\Exception $e) {
            // 数据库操作失败 输出错误信息
            $resultInfo = CommonHelper::stdmessage(0, $e->getMessage());
        }
        return json($resultInfo);
    }

    /**
     * 删除指定资源
     */
    public function delete($id)
    {
        $id = CommonHelper::parseTextIds($id);
        if ($id) {
            $result = AdminModel::destroy($id);
        } else {
            $result = false;
        }
        return json(CommonHelper::stdmessage($result ? 1 : 0, \'\'));
    }
    /**
     * 检测资源是否存在
     */
    public function checkExists(Request $request)
    {
        $name = $request->param(\'name\');
        $id = 0;
        if ($name) {
            $id = AdminModel::where(\'user_name\', $name)->value(\'id\');
        }
        if ($id) {
            return json(CommonHelper::stdmessage(1, \'\', $id));
        } else {
            return json(CommonHelper::stdmessage(0, \'查无记录\'));
        }
    }
}

管理员 模型类代码示例

<?php

declare(strict_types=1);

namespace app\model;

use think\Model;
use app\helper\MailHelper;
use app\helper\StringHelper;
use app\helper\CommonHelper;

/**
 * 权限管理员模型类
 */
class AdminModel extends Model
{
    // 设置当前模型对应的完整数据表名称
    protected $table = \'tbl_admin\';

    // 自动时间戳
    protected $autoWriteTimestamp = \'int\';

    // 定义时间戳字段名
    protected $createTime = \'created_at\';
    protected $updateTime = \'updated_at\';

    /**
     * status 字段内容
     */
    protected $status_data = [\'无效\', \'有效\'];

    /**
     * 返回 status 字段内容
     */
    public function getStatusData()
    {
        return $this->status_data;
    }

    /**
     * 返回 status 字段获取器的值
     */
    public function getStatusTextAttr($value)
    {
        $value = intval($this->data[\'status\']);
        return isset($this->status_data[$value]) ? $this->status_data[$value] : $value;
    }

    /**
     * 返回 login_time 字段获取器的值
     */
    public function getLoginTimeTextAttr()
    {
        return date(\'Y-m-d H:i:s\', $this->login_time);
    }

    /**
     * 登录操作
     */
    public static function login($account, $password, $ip, $field = \'*\')
    {
        if (!$account || !$password) {
            return CommonHelper::stdmessage(0, \'账号和密码是必填项\');
        }
        // 登录查询字段
        $model = self::where(\'user_name|mobile|email\', $account)->field($field)->find();
        if (!$model) {
            return CommonHelper::stdmessage(0, \'用户不存在\');
        }
        if (!$model->status) {
            return CommonHelper::stdmessage(0, \'用户已被锁定\');
        }
        $hashPassowrd = CommonHelper::hashPassword($password, $model->salt);
        if ($hashPassowrd != $model->password) {
            return CommonHelper::stdmessage(0, \'密码错误\');
        }
        // 记录登录时间和IP
        self::where(\'id\', $model->id)->inc(\'login_count\', 1)->update([\'login_time\' => time(), \'login_ip\' => $ip]);
        return CommonHelper::stdmessage(1, \'\', $model->toArray());
    }
    /**
     * 重置密码
     */
    public static function resetPassword($email)
    {
        $model = self::where(\'email\', $email)->field(\'user_name, salt\');
        if (!$model) {
            return CommonHelper::stdmessage(0, \'用户不存在\');
        }
        $password = StringHelper::newAlphaNum(8);
        $model->salt = StringHelper::newAlpha(6);
        $model->password = CommonHelper::hashPassword($password, $model->salt);
        $model->save();

        $mailModel = new MailHelper();
        $mailModel->initByConfig();
        $msgInfo = $mailModel->send(\'密码重置\', "尊敬的{$model->user_name},<p>您的密码已被重置为 {$password},请尽快登录网站修改您的新密码。</p>", $email);
        return $msgInfo;
    }

    /**
     * 添加一条记录
     */
    public static function createRecord($userName, $password, $ip, $roleName = \'\')
    {
        $id = self::where(\'user_name\', $userName)->value(\'id\');
        if ($id) {
            return CommonHelper::stdmessage(0, \'账号已存在\');
        }
        $salt = StringHelper::newAlpha(6);
        $data = [
            \'user_name\' => $userName,
            \'password\' => CommonHelper::hashPassword($password, $salt),
            \'salt\' => $salt,
            \'role_name\' => $roleName,
            \'register_ip\' => $ip,
            \'status\' => 1,
        ];
        $model = self::create($data);
        if ($model) {
            return CommonHelper::stdmessage(1, \'创建成功\', $model->id);
        } else {
            return CommonHelper::stdmessage(0, \'创建失败\');
        }
    }
}

定时任务功能示例

<?php

/**
 * 定时任务 命令类
 */

declare(strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Cache;
use app\model\CrontabModel;
use app\model\AutoTaskModel;

/**
 * 定时任务 命令类
 * 主要功能:定时执行SQL;定时请求项目URL或外部URL;定时清空缓存
 * 说明:此功能不支持 Windows 系统,需要结合 Linux 的 Crontab 才可以正常使用,可以定时执行一系列的操作。
 * 准备工作:Linux 下使用 crontab -e -u [用户名] 添加一条记录(这里的用户名是指 Apache 或 Nginx 的执行用户,一般为 www 或 nginx)
 * 命令示例:
 * 命令:crontab -e -u www (以 www 用户编辑 crontab 文件)
 * 粘帖:* * * * * /usr/bin/php /www/yoursite/think autotask > /dev/null  2>&1 &
 * 命令:systemctl restart crond.service
 * 命令:crontab -l -u www
 */
class AutoTask extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName(\'autotask\')
            ->setDescription(\'the autotask command\');
    }

    protected function execute(Input $input, Output $output)
    {
        // 指令输出
        $output->writeln(\'autotask\');
        file_put_contents(runtime_path() . \'auto_task.log\', date(\'Y-m-d H:i:s\') . PHP_EOL, FILE_APPEND);
        AutoTaskModel::run();
    }
}

分类:

技术点:

相关文章: