Dcr163的博客 - 日志
type: channel
link: https://www.dcr163.cn/category/日志
items: Thinkphp6接入Paypal支付流程
items: Thinkphp8.0使用Rabbitmq的做队列和延迟队列代码实例
items: php使用usort排序多维数组内的值
items: PHP8编译安装Swoole6.0.2
items: Centos8搭建LNMP环境
items: 安装PHP8报错：configure: error: iconv does not support errno
items: 在PHP中下载网络图片尽量用CURL，不要用file_get_content
items: 生产环境LNMP环境优化配置，高并发配置
items: acme.sh 实现生成免费证书并且自动续签
items: Mysqldump 指定字符集导出数据库

Thinkphp6接入Paypal支付流程
type: item
date: 2025-10-09T16:36:00+08:00
description: 直接进入主题这里直接在默认的Index控制器中编写的测试代码<?php /** * Created by PhpStorm. * Author: Dcr163 * Date: 2025/10/10 * Time: 15:49 * paypal服务类 */ namespace app\customer\services; use Exception; use think\App; use think\facade\Log; class PaypalService { //paypal 沙盒环境 const PAYPAL_URL = 'https://api-m.sandbox.paypal.com'; //paypal 正式环境 // const PAYPAL_URL = 'https://api-m.paypal.com'; //获取Token 链接 沙盒环境 const GET_TOKEN_URL = self::PAYPAL_URL . '/v1/oauth2/token'; //订单创建URL const CREATE_ORDER_URL = self::PAYPAL_URL . '/v2/checkout/orders'; //get:显示订单详情 patch:更新状态为“已创建”或“已批准”的订单。无法更新状态为“已完成”的订单 /v2/checkout/orders/{id} const SHOW_ORDER_URL = self::PAYPAL_URL . '/v2/checkout/orders/'; //API KEY 这里面对应https://developer.paypal.com/dashboard/applications/sandbox private $clientId; //API秘钥 这里面对应https://developer.paypal.com/dashboard/applications/sandbox private $clientSecret; //paypal通信token public $paypalToken = ''; //paypal auth token缓存key const TOKEN_KEY = 'paypal:auth:token'; //webhook值 https://developer.paypal.com/dashboard/applications app管理底部webhook板块 private $webhookId; //webhook 验证证书 private $certCache = []; //webhook验证证书保存地址 private $cacheDir = ''; public function __construct(array $config) { $this->clientId = $config['client_id'] ?? ''; $this->clientSecret = $config['client_secret'] ?? ''; } /** * 获取paypal通信 Token * @param bool $refresh 是否强制刷新token * @return void * @throws Exception */ public function getToken(bool $refresh = false) { //先读取缓存 $token = redisUser(14)->get(self::TOKEN_KEY); if (!$token || $refresh) { $res = $this->request(self::GET_TOKEN_URL, 'POST', http_build_query(['grant_type' => 'client_credentials']), '', 10, '', [$this->clientId, $this->clientSecret]); $data = json_decode($res['content'], true); if (isset($data['error']) || !isset($data['access_token'])) { throw new Exception($data); } //把token给缓存下来，设置过期时间早与paypal过期时间 redisUser(14)->setex(self::TOKEN_KEY, $data['expires_in'] - 10, $data['access_token']); $token = $data['access_token']; } $this->paypalToken = $tok…

Thinkphp8.0使用Rabbitmq的做队列和延迟队列代码实例
type: item
date: 2025-09-28T11:22:54+08:00
description: 前提是已经安装好Rabbitmq的服务和安装好Rabbitmq消息延迟队列插件新建数据表使用的orders表结构CREATE TABLE `orders` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `orderno` varchar(60) NOT NULL DEFAULT '' COMMENT '订单号', `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '价格', `create_time` datetime DEFAULT NULL, `cancel_time` datetime DEFAULT NULL, `status` tinyint(3) DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=75 DEFAULT CHARSET=utf8mb4 COMMENT '测试订单表';添加控制器在app模块新增一个控制器/app/controller/Order.php,内容如下：<?php declare (strict_types=1); namespace app\controller; use app\service\RabbitmqService; use Exception; use think\Response; use think\response\Json; class Order { /** * * 模拟客户下单 * @return Response */ public function index() { $db = db('orders'); $getConnection = $db->getConnection(); $timeOut = 1800; $id = $getConnection->transaction(function () use ($db, $timeOut) { $time = time(); $orderData = [ 'orderno' => uniqid('hm'), 'price' => mt_rand(10, 999), 'create_time' => date('Y-m-d H:i:s', $time), 'status' => 0, ]; $id = $db->insertGetId($orderData); $rabbitmqService = app()->make(RabbitmqService::class); $rabbitmqService->sendDelayedMsg([ 'id' => $id, ], $timeOut); return $id; }); if ($id) { $msg = sprintf('订单插入成功：【%d】;在 %f 分钟内未支付，将自动取消订单~', $id, $timeOut/60); return json(['msg' => $msg]); } return json(['msg' => 'fail']); } /** * 模拟异步发送邮件 * @return Json * @throws Exception */ public function email() { $mailData = [ 'email' => mt_rand(10000000, 999999999) . '@qq.com', ]; $rabbitmqService = app()->make(RabbitmqService::class); $res = $rabbitmqService->sendMsg($mailData); return $res ? json(['msg' => $mailData['email']]) : json(['msg' => 'fail']); } }添加路由修改位置：/route/app.php,新增2个访问的地址Route::group('order',function(){ Route::get('index', Order::class.'::index'); Route::get('email', Order::class.'::email'); });添加订单取消和发送邮件的操作脚本主要是用在php think xx 命令行中执行新建订单取消命令行脚本新建文件：/app/command/OrderCancel.php,文件内容如下：<?php declare (strict_types=1); namespace app\command; use app\service\RabbitmqService;…

php使用usort排序多维数组内的值
type: item
date: 2025-09-11T23:38:12+08:00
description: 直接上代码：$data = [ '一队' => [ 'counts' => 0, 'totals' => 0, 'team' => [ ['realname' => '张三', 'counts' => 10, 'totals' => 600], ['realname' => '张三2', 'counts' => 17, 'totals' => 478], ['realname' => '张三3', 'counts' => 190, 'totals' => 10000], ['realname' => '张三4', 'counts' => 8, 'totals' => 500], ] ], '二队' => [ 'counts' => 0, 'totals' => 0, 'team' => [ ['realname' => '李四', 'counts' => 10, 'totals' => 600], ['realname' => '李四2', 'counts' => 17, 'totals' => 478], ['realname' => '李四3', 'counts' => 190, 'totals' => 10000], ['realname' => '李四4', 'counts' => 8, 'totals' => 500], ] ], '三队' => [ 'counts' => 0, 'totals' => 0, 'team' => [ ['realname' => '王五', 'counts' => 10, 'totals' => 600], ['realname' => '王五2', 'counts' => 17, 'totals' => 478], ['realname' => '王五3', 'counts' => 190, 'totals' => 10000], ['realname' => '王五4', 'counts' => 8, 'totals' => 500], ['realname' => '王五5', 'counts' => 11, 'totals' => 600], ] ], ]; //数组根据指定key排序 foreach ($data as $key => &$val) { usort($val['team'], function ($a, $b) { //根据totals从高到低排序 if ($a['totals'] != $b['totals']) { if ($a['totals'] > $b['totals']) { // 如果是 < 就是从低到高排序 return -1; } elseif ($a['totals'] == $b['totals']) { return 0; } else { return 1; } //php7+可以这么写,使用航天飞船符 <=> 更简洁，逻辑清晰 //return $b['totals'] <=> $a['totals']; } //根据counts从高到底排序 if ($a['counts'] > $b['counts']) { // 如果是 < 就是从低到高排序 return -1; } elseif ($a['counts'] == $b['counts']) { return 0; } else { return 1; } //php7+可以这么写,使用航天飞船符 <=> 更简洁，逻辑清晰 //return $b['counts'] <=> $a['counts']; }); } unset($val); echo ' '; print_r($data); 看看最终输出的结果：Array ( [一队] => Array ( [counts] => 0 [totals] => 0 [team] => Array ( [0] => Array ( [realname] => 张三3 [counts] => 190 [totals] => 10000 ) [1] => Array ( [realname] => 张三 [counts] => 10 [totals] => 600 ) [2] => Array ( [realname] => 张三4 [counts] => 8 [totals] => 500 ) [3] => Array ( [realname] => 张三2 [counts] => 17 [totals] => 478 ) ) ) [二队] => Array ( [counts] => 0 [totals] => 0 [team] => Array ( [0] => Array ( [realname] => 李四3 [counts…

PHP8编译安装Swoole6.0.2
type: item
date: 2025-06-21T17:09:00+08:00
description: 环境Centos8PHP8.4.8Swoole6.0.2下载Swoole6.0.2使用的Github地址下载（比较新），下载地址：https://github.com/swoole/swoole-src可以下载zip或源码都行，我这边下载好了.zip解压:unzip swoole-src-master.zip进入到解压后的目录可以看到解压出来的是没有configure这个执行文件的，我们需要用php8的phpize命令生成下，我这边php8安装目录在/usr/local/php8/直接在当前目录执行命令：/usr/local/php8/bin/phpize执行完毕后就发现生成了这个文件了：configure开始执行configure执行命令：./configure --with-php-config=/usr/local/php8/bin/php-config这个选项--with-php-config一定要匹配到对应的目录编译并且安装执行命令：make && make install如果不出意外的话，就编译好了swoole.so这个文件到php8的扩展目录下在php.ini中启用swoole扩展编辑配置文件：vim /usr/local/php8/etc/php.ini追加这个拓展：extension=swoole保存后，使用php -m验证是否成功加载swoole以上就是PHP8安装swoole拓展的步骤了。

Centos8搭建LNMP环境
type: item
date: 2025-06-05T16:08:00+08:00
description: 先新建一个软件目录，我们全部放在lnmp这个目录中mkdir lnmp && cd lnmp下载Nginx先去官方网站下载http://nginx.org/en/download.html，这里我们选择最新版下载 http://nginx.org/download/nginx-1.23.4.tar.gz[root@centos8_1 lnmp]# wget http://nginx.org/download/nginx-1.23.4.tar.gz下载Mysql，这里我们使用Mysql8.0.33下载地址：https://cdn.mysql.com/Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz[root@centos8_1 lnmp]# wget https://cdn.mysql.com/Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz下载PHP7.4.33下载地址：https://www.php.net/distributions/php-7.4.33.tar.gz[root@centos8_1 lnmp]# wget https://www.php.net/distributions/php-7.4.33.tar.gz顺便提供下其他版本的PHP下载地址：PHP7.0.33 https://www.php.net/distributions/php-7.0.33.tar.gzPHP7.2.34 https://www.php.net/distributions/php-7.2.34.tar.gzPHP7.4.33 https://www.php.net/distributions/php-7.4.33.tar.gz下载rpcsvc-proto[root@centos8_1 lnmp]# wget https://www.linuxprobe.com/Software/rpcsvc-proto-1.4.tar.gz系统要有具备编译程序源码的环境，把常用的软件包都给安装上[root@centos8_1 lnmp]# dnf -y install apr* autoconf automake numactl bison bzip2-devel cpp curl-devel fontconfig-devel freetype-devel gcc gcc-c++ gd-devel gettext-devel kernel-headers keyutils-libs-devel krb5-devel libcom_err-devel libpng-devel libjpeg* libsepol-devel libselinux-devel libstdc++-devel libtool* libxml2-devel libXpm* libxml* libXaw-devel libXmu-devel libtiff* make openssl-devel patch pcre-devel perl php-common php-gd telnet zlib-devel libtirpc-devel gtk* ntpstat na* bison* lrzsz cmake ncurses-devel libzip-devel libxslt-devel gdbm-devel readline-devel gmp-devel安装配置Nginx服务创建用于管理网站服务的系统账户，新建账户时应使用-M参数不创建对应的家目录，-s参数指定登录后的Shell解释器为/sbin/nologin，确保任何人都不能通过这个账号登录主机。[root@centos8_1 lnmp]# useradd nginx -M -s /sbin/nologin编译安装Nginx网站服务程序。为了能够让网站服务支持更多的功能，需要在编译过程中添加额外的参数，其中较为重要的是使用prefix参数指定服务将被安装到哪个目录，方便后面找到和调用它。其次，考虑到HTTPS协议的使用越来越广泛，所以这里用with-http_ssl_module参数来开启Nginx服务的SSL加密模块，以便日后开启HTTPS协议功能：[root@centos8_1 lnmp]# tar -zxvf nginx-1.24.0.tar.gz && cd nginx-1.24.0编译[root@centos8_1 lnmp]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module 编译后…

安装PHP8报错：configure: error: iconv does not support errno
type: item
date: 2025-06-04T16:57:00+08:00
description: 遇到 configure: error: iconv does not support errno 错误时，通常与 libiconv 库的版本或配置有关。以下是详细的解决方案：安装或升级 libiconvPHP 8 对 libiconv 的兼容性要求较高，需安装 GNU 的 libiconv（非系统自带的 glibc 的 iconv）。步骤：1.下载 libiconv 源码wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz2.解压并编译安装tar -zxvf libiconv-1.16.tar.gz cd libiconv-1.16 ./configure --prefix=/usr/local/libiconv make && make install

在PHP中下载网络图片尽量用CURL，不要用file_get_content
type: item
date: 2025-05-15T14:07:46+08:00
description: 例如：https://thirdwx.qlogo.cn/mmopen/vi_32/jDiauSBiauFCicDp9CBMjqNLfuaUibXQHciaaESNwvZhK2FQXTBJcvYwYcyOWrOmKO7otewTphq59g4J2GPfxE8DdNQ/132如果用file_get_contents函数下载保存，会影响程序的运行速度，快则几秒，多则几十秒这里建议用CURL操作：/** * 下载头像 * @param $url * @param $save_path * @param $openid * @return false|string[] */ private function saveWechatAvatar($url, $save_path, $openid) { // 创建保存目录 $absolute_path = $save_path; if (!file_exists($absolute_path)) { mkdir($absolute_path, 0755, true); } $filename = $openid . '.jpg'; $file_path = $absolute_path . '/' . $filename; if (is_file($file_path)) { return [ 'file_path' => $file_path, 'filename' => $filename, ]; } // 配置 CURL $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_ENCODING => 'gzip', // 压缩传输优化 CURLOPT_HTTPHEADER => [ 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36', 'Accept-Encoding: gzip, deflate' ], CURLOPT_SSL_VERIFYPEER => false, // 跳过 SSL 验证 CURLOPT_TIMEOUT => 10 // 设置超时时间 ]); $image_data = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 保存图片 if ($http_code == 200 && !empty($image_data)) { return file_put_contents($file_path, $image_data) ? [ 'file_path' => $file_path, 'filename' => $filename, ] : false; } return false; }实际应用CURL获取图片，基本不影响程序使用，体验感大大提升。

生产环境LNMP环境优化配置，高并发配置
type: item
date: 2025-05-14T16:55:03+08:00
description: 初始化服务器后，先挂载数据盘为数据盘创建分区fdisk -lu为数据盘创建分区yum install -y parted #开始分区 /dev/vdb 这个磁盘替换成自己的设备名称 parted /dev/vdb #在Parted交互命令行输入以下内容，设置GPT分区格式。 mklabel gpt #划分一个主分区，并设置分区的开始位置和结束位置。 mkpart primary 1 100% #检查分区是否对齐 align-check optimal 1 #是对齐的，结果如下所示： 1 aligned #查看分区表 print #退出Parted工具 quit #系统重读分区表 partprobe #查看新分区信息 fdisk -lu /dev/vdb分区创建文件系统yum install -y e2fsprogs #运行以下命令，创建文件系统 mkfs -t ext4 /dev/vdb配置开机自动挂载分区#备份/etc/fstab文件 cp /etc/fstab /etc/fstab.bak #修改/etc/fstab文件 echo `blkid /dev/vdb | awk '{print $2}' | sed 's/\"//g'` /www ext4 defaults 0 0 >> /etc/fstab #查看/etc/fstab中的新分区信息 cat /etc/fstab #挂载/etc/fstab配置的文件系统。如果无报错输出，表示您的/etc/fstab配置无误 mount -a #检查挂载结果是否符合 df -h采用lnmp.org一键安装环境采用阿里云Centos7.9系统，安装前可以根据自己的需求，修改安装脚本里的内容直接上传本目录lnmp1.9.tar.gz一键安装包到服务器上文件夹内部的源码包，是放在lnmp1.9/src这个目录下的，避免再使用网络下载比较费时间安装 ./install.sh lnmp单服务高并发配置文件说明本文件夹内的配置文件在服务器24 vCPU 96 GiB 配置的服务器可以直接使用，2000个并服务器主要修改的内容有 ## 打开文件数量 ulimit -a ulimit -n 65536 #永久修改 #修改文件cat /etc/security/limits.conf,这个值可以改大点，不会影响什么 # End of file root soft nofile 655350 root hard nofile 655350 * soft nofile 655350 * hard nofile 655350 # TCP最大连接数 cat /proc/sys/net/core/somaxconn echo 20000 > /proc/sys/net/core/somaxconn ##TCP连接立即回收、回用 cat /proc/sys/net/ipv4/tcp_tw_reuse ##我的结果是1，不需要改了 cat /proc/sys/net/ipv4/tcp_tw_recycle ##我的结果是1，不需要改了 echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle cat /proc/sys/net/ipv4/tcp_syncookies ##我的结果是1 echo 0 > /proc/sys/net/ipv4/tcp_syncookies ## 屏蔽TCP异步Nginx配置信息user www www; worker_processes auto; worker_cpu_affinity auto; error_log /vdb1/wwwlogs/nginx_error.log crit; pid /usr/local/nginx/logs/nginx.pid; #Specifies the value for maximum file descriptors that can be opened by this process. worker_rlimit_nofile 51200; events { use epoll; worker_connections 51200; multi_accept off; accept_mutex off; } http { include mime.types; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 3…

acme.sh 实现生成免费证书并且自动续签
type: item
date: 2024-12-28T15:37:00+08:00
description: acme.sh 实现了 acme 协议，可以从 ZeroSSL，Let's Encrypt 等 CA 生成免费的证书1. 安装 acme.sh安装很简单，一条命令:curl https://get.acme.sh | sh -s email=my@example.com或者wget -O - https://get.acme.sh | sh -s email=my@example.com普通用户和 root 用户都可以安装使用。安装过程进行了以下几步:把 acme.sh 安装到你的 home 目录下:~/.acme.sh/并创建 一个 shell 的 alias，例如 .bashrc，方便你的使用: alias acme.sh=~/.acme.sh/acme.sh自动为你创建 cronjob， 每天 0:00 点自动检测所有的证书，如果快过期了，需要更新，则会自动更新证书。更高级的安装选项请参考: https://github.com/acmesh-official/acme.sh/wiki/How-to-install安装过程不会污染已有的系统任何功能和文件，所有的修改都限制在安装目录中: ~/.acme.sh/注意：如果安装完成后提示 -bash: acme.sh: command not found，需要手动执行 source ~/.bashrc2. 生成证书acme.sh 实现了 acme 协议支持的所有验证协议。一般有两种方式验证: HTTP 和 DNS 验证。下面我就用http验证来实现，DNS请点击参考：https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E#dns-%E9%AA%8C%E8%AF%81HTTP 验证这里优秀修改默认 CAacme.sh 脚本默认 CA 服务器是 ZeroSSL，有时可能会导致获取证书的时候一直出现：Pending，The CA is processing your order，please just wait.只需要把 CA 服务器改成 Let's Encrypt 即可，虽然更改以后还是有概率出现 pending，但基本 2-3 次即可成功acme.sh --set-default-ca --server letsencrypt更高级的用法请参考: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert直接签发只需要指定域名，并指定域名所在的网站根目录. acme.sh 会全自动的生成验证文件，并放到网站的根目录，验证完成后会聪明的删除验证文件，整个过程没有任何副作用。acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/使用 Apache 模式如果你用的 Apache 服务器，acme.sh 还可以智能的从 Apache 的配置中自动完成验证，你不需要指定网站根目录:acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com使用 Nginx 模式如果你用的 Nginx 服务器，或者反代，acme.sh 还可以智能的从 Nginx 的配置中自动完成验证，你不需要指定网站根目录:acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com注意，无论是 Apache 还是 Nginx 模式，acme.sh 在完成验证之后，会恢复到之前的状态，都不会私自更改程序本身的配置. 好处是你不用担心配置被搞坏，也有一个缺点，你需要自己配置 SSL 项，否则只能成功生成证书，你的网站还是无法正常使用 HTTPS。使用独立服务模式如果服务器上没有运行任何 Web 服务，80 端口是空闲的，那么 acme.sh 还能假装自己是一个 WebServer，临时监听 80 端口，完成验证:acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com3. 复制证书证书生成好以后，我们需要把证书复制给对应的 Apache、Nginx 或其他服务器去使用，记得再Apache或Nginx中先开启ssl配置。必须使用 --install-cert 命令来把证书复制到目标文件，请勿直接使用 ~/.acme.sh/ 目录下的证书文件，这里面的文件都是内部使用，而且…

Mysqldump 指定字符集导出数据库
type: item
date: 2024-12-27T15:15:00+08:00
description: 可以在运行 mysqldump 时通过命令行参数来指定字符集。例如：mysqldump --default-character-set=utf8mb4 --single-transaction --quick --lock-tables=false -u your_username -p your_database > backup.sql这个命令做了以下几件事：--default-character-set=utf8mb4: 指定导出文件的字符集为 utf8mb4--single-transaction: 确保导出的数据是一致的快照（对于支持事务的存储引擎）--quick: 强制 mysqldump 从服务器读取行并立即写入到标准输出，而不是先将整个结果集读入内存。--lock-tables=false: 避免锁定所有表，这对于大型数据库或高并发环境中的导出非常重要。请记得替换 your_username、your_password 和 your_database 为实际的用户名、密码和数据库名称。注意事项确保你的数据库、表和列都设置了正确的字符集 (utf8mb4) 和排序规则 (collation)，以避免在导出和导入过程中出现字符编码问题。如果你的数据库已经存在并且不是 utf8mb4，你可能需要转换现有的数据库、表和列的字符集。