首页
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
Search
1
【PHP】PHPoffice/PHPSpreadsheet读取和写入Excel
1,700 阅读
2
【Layui】控制页面元素展示隐藏
1,551 阅读
3
【Git】No tracked branch configured for branch master or the branch doesn't exist.
1,489 阅读
4
【PHP】PHP实现JWT生成和验证
1,397 阅读
5
精准检测,助力社交管理 —— 微信好友检测服务来袭!
1,299 阅读
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
登录
Search
标签搜索
PHP
函数
方法
类
MySQL
ThinkPHP
JavaScript
OOP
Layui
Web
Server
Docker
Linux
PHPSpreadsheet
PHPoffice
Array
设计模式
Nginx
Git
排序算法
小破孩
累计撰写
253
篇文章
累计收到
13
条评论
首页
栏目
默认分类
PHP
ThinkPHP
Laravel
面向对象
设计模式
算法
基础
网络安全
Web
HTML
CSS
JavaScript
jQuery
Layui
VUE
uni-app
Database
MySQL
Redis
RabbitMQ
Nginx
Git
Linux
Soft Ware
Windows
网赚
Go
Docker
页面
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
搜索到
249
篇与
的结果
2025-08-20
【Redis】Redis的操作方法整理
<?php // 配置连接的IP、端口、以及相应的数据库 $server = array( 'host' => '127.0.0.1', 'port' => 6379, 'database' => 15 ); $redis = new Client($server); // 普通set/get操作 $redis->set('library', 'predis'); $retval = $redis->get('library'); echo $retval; // 显示 'predis' // setex set一个存储时效 $redis->setex('str', 10, 'bar'); // 表示存储有效期为10秒 // setnx/msetnx相当于add操作,不会覆盖已有值 $redis->setnx('foo', 12); // true $redis->setnx('foo', 34); // false // getset操作,set的变种,结果返回替换前的值 $redis->getset('foo', 56); // 返回34 // incrby/incr/decrby/decr 对值的递增和递减 $redis->incr('foo'); // foo为57 $redis->incrby('foo', 2); // foo为59 // exists检测是否存在某值 $redis->exists('foo'); // true // del 删除 $redis->del('foo'); // true // type 类型检测,字符串返回string,列表返回 list,set表返回set/zset,hash表返回hash $redis->type('foo'); // 不存在,返回none $redis->set('str', 'test'); $redis->type('str'); // 字符串,返回string // append 连接到已存在字符串 $redis->append('str', '_123'); // 返回累加后的字符串长度8,此进str为 'test_123' // setrange 部分替换操作 $redis->setrange('str', 0, 'abc'); // 返回3,参数2为0时等同于set操作 $redis->setrange('str', 2, 'cd'); // 返回4,表示从第2个字符后替换,这时'str'为'abcd' // substr 部分获取操作 $redis->substr('str', 0, 2); // 表示从第0个起,取到第2个字符,共3个,返回'abc' // strlen 获取字符串长度 $redis->strlen('str'); // 返回4 // setbit/getbit 位存储和获取 $redis->setbit('binary', 31, 1); // 表示在第31位存入1,这边可能会有大小端问题?不过没关系,getbit 应该不会有问题 $redis->getbit('binary', 31); // 返回1 // keys 模糊查找功能,支持*号以及?号(匹配一个字符) $redis->set('foo1', 123); $redis->set('foo2', 456); $redis->keys('foo*'); // 返回foo1和foo2的array $redis->keys('f?o?'); // 同上 // randomkey 随机返回一个key $redis->randomkey(); // 可能是返回 'foo1'或者是'foo2'及其它任何一存在redis的key // rename/renamenx 对key进行改名,所不同的是renamenx不允许改成已存在的key $redis->rename('str', 'str2'); // 把原先命名为'str'的key改成了'str2' // expire 设置key-value的时效性,ttl 获取剩余有效期,persist 重新设置为永久存储 $redis->expire('foo', 1); // 设置有效期为1秒 $redis->ttl('foo'); // 返回有效期值1s $redis->expire('foo'); // 取消expire行为 // dbsize 返回redis当前数据库的记录总数 $redis->dbsize(); /* * 队列操作 */ // rpush/rpushx 有序列表操作,从队列后插入元素 // lpush/lpushx 和rpush/rpushx的区别是插入到队列的头部,同上,'x'含义是只对已存在的key进行操作 $redis->rpush('fooList', 'bar1'); // 返回一个列表的长度1 $redis->lpush('fooList', 'bar0'); // 返回一个列表的长度2 $redis->rpushx('fooList', 'bar2'); // 返回3,rpushx只对已存在的队列做添加,否则返回0 // llen返回当前列表长度 $redis->llen('fooList'); // 3 // lrange 返回队列中一个区间的元素 $redis->lrange('fooList', 0, 1); // 返回数组包含第0个至第1个共2个元素 $redis->lrange('fooList', 0, -1); // 返回第0个至倒数第一个,相当于返回所有元素,注意redis中很多时候会用到负数,下同 // lindex 返回指定顺序位置的list元素 $redis->lindex('fooList', 1); // 返回'bar1' // lset 修改队列中指定位置的value $redis->lset('fooList', 1, '123'); // 修改位置1的元素,返回true // lrem 删除队列中左起指定数量的字符 $redis->lrem('fooList', 1, '_'); // 删除队列中左起(右起使用-1)1个字符'_'(若有) // lpop/rpop 类似栈结构地弹出(并删除)最左或最右的一个元素 $redis->lpop('fooList'); // 'bar0' $redis->rpop('fooList'); // 'bar2' // ltrim 队列修改,保留左边起若干元素,其余删除 $redis->ltrim('fooList', 0, 1); // 保留左边起第0个至第1个元素 // rpoplpush 从一个队列中pop出元素并push到另一个队列 $redis->rpush('list1', 'ab0'); $redis->rpush('list1', 'ab1'); $redis->rpush('list2', 'ab2'); $redis->rpush('list2', 'ab3'); $redis->rpoplpush('list1', 'list2'); // 结果list1 => array('ab0'), list2 => array('ab1','ab2','ab3') $redis->rpoplpush('list2', 'list2'); // 也适用于同一个队列,把最后一个元素移到头部 list2 => array('ab3','ab1','ab2') // linsert 在队列的中间指定元素前或后插入元素 $redis->linsert('list2', 'before', 'ab1', '123'); // 表示在元素'ab1'之前插入'123' $redis->linsert('list2', 'after', 'ab1', '456'); // 表示在元素'ab1'之后插入'456' // blpop/brpop 阻塞并等待一个列队不为空时,再pop出最左或最右的一个元素(这个功能在php以外可以说非常好用) // brpoplpush 同样是阻塞并等待操作,结果同rpoplpush一样 $redis->blpop('list3', 10); // 如果list3为空则一直等待,直到不为空时将第一元素弹出,10秒后超时 /** * set表操作 */ // sadd 增加元素,返回true,重复返回false $redis->sadd('set1', 'ab'); $redis->sadd('set1', 'cd'); $redis->sadd('set1', 'ef'); // srem 移除指定元素 $redis->srem('set1', 'cd'); // 删除'cd'元素 // spop 弹出首元素 $redis->spop('set1'); // smove 移动当前set表的指定元素到另一个set表 $redis->sadd('set2', '123'); $redis->smove('set1', 'set2', 'ab'); // 移动'set1'中的'ab'到'set2',返回true or false // scard 返回当前set表元素个数 $redis->scard('set2'); // 2 // sismember 判断元素是否属于当前表 $redis->sismember('set2', '123'); // true or false // smembers 返回当前表的所有元素 $redis->smembers('set2'); // array('123','ab') // sinter/sunion/sdiff 返回两个表中元素的交集/并集/补集 $redis->sadd('set1', 'ab'); $redis->sinter('set2', 'set1'); // 返回array('ab') // sinterstore/sunionstore/sdiffstore 将两个表交集/并集/补集元素copy到第三个表中 $redis->set('foo', 0); $redis->sinterstore('foo', 'set1'); // 这边等同于将'set1'的内容copy到'foo'中,并将'foo'转为set表 $redis->sinterstore('foo', array('set1', 'set2')); // 将'set1'和'set2'中相同的元素copy到'foo'表中,覆盖'foo'原有内容 // srandmember 返回表中一个随机元素 $redis->srandmember('set1'); /** * 有序set表操作 */ // zadd 增加元素,并设置序号,返回true,重复返回false $redis->zadd('zset1', 1, 'ab'); $redis->zadd('zset1', 2, 'cd'); $redis->zadd('zset1', 3, 'ef'); // zincrby 对指定元素索引值的增减,改变元素排列次序 $redis->zincrby('zset1', 10, 'ab'); // 返回11 // zrem 移除指定元素 $redis->zrem('zset1', 'ef'); // true or false // zrange 按位置次序返回表中指定区间的元素 $redis->zrange('zset1', 0, 1); // 返回位置0和1之间(两个)的元素 $redis->zrange('zset1', 0, -1); // 返回位置0和倒数第一个元素之间的元素(相当于所有元素) // zrevrange 同上,返回表中指定区间的元素,按次序倒排 $redis->zrevrange('zset1', 0, -1); // 元素顺序和zrange相反 // zrangebyscore/zrevrangebyscore 按顺序/降序返回表中指定索引区间的元素 $redis->zadd('zset1', 3, 'ef'); $redis->zadd('zset1', 5, 'gh'); $redis->zrangebyscore('zset1', 2, 9); // 返回索引值2-9之间的元素 array('ef','gh') // 参数形式 $redis->zrangebyscore('zset1', 2, 9, 'withscores'); // 返回索引值2-9之间的元素并包含索引值 array(array('ef',3),array('gh',5)) $redis->zrangebyscore('zset1', 2, 9, array( 'withscores' => true, 'limit' => array(1, 2) )); // 返回索引值2-9之间的元素,'withscores'=>true表示包含索引值; 'limit'=>array(1,2),表示最多返回2条,结果为array(array('ef',3),array('gh',5)) // zunionstore/zinterstore 将多个表的并集/交集存入另一个表中 $redis->zunionstore('zset3', array('zset1', 'zset2', 'zset0')); // 将'zset1','zset2','zset0'的并集存入'zset3' // 其它参数 $redis->zunionstore('zset3', array('zset1', 'zset2'), array( 'weights' => array(5, 0) )); // weights参数表示权重,其中表示并集后值大于5的元素排在前,大于0的排在后 $redis->zunionstore('zset3', array('zset1', 'zset2'), array( 'aggregate' => 'max' )); // 'aggregate' => 'max'或'min'表示并集后相同的元素是取大值或是取小值 // zcount 统计一个索引区间的元素个数 $redis->zcount('zset1', 3, 5); // 2 $redis->zcount('zset1', '(3', 5); // '(3'表示索引值在3-5之间但不含3,同理也可以使用'(5'表示上限为5但不含5 // zcard 统计元素个数 $redis->zcard('zset1'); // 4 // zscore 查询元素的索引 $redis->zscore('zset1', 'ef'); // 3 // zremrangebyscore 删除一个索引区间的元素 $redis->zremrangebyscore('zset1', 0, 2); // 删除索引在0-2之间的元素('ab','cd'),返回删除元素个数2 // zrank/zrevrank 返回元素所在表顺序/降序的位置(不是索引) $redis->zrank('zset1', 'ef'); // 返回0,因为它是第一个元素;zrevrank则返回1(最后一个) // zremrangebyrank 删除表中指定位置区间的元素 $redis->zremrangebyrank('zset1', 0, 10); // 删除位置为0-10的元素,返回删除的元素个数2 /** * hash表操作 */ // hset/hget 存取hash表的数据 $redis->hset('hash1', 'key1', 'v1'); // 将key为'key1' value为'v1'的元素存入hash1表 $redis->hset('hash1', 'key2', 'v2'); $redis->hget('hash1', 'key1'); // 取出表'hash1'中的key 'key1'的值,返回'v1' // hexists 返回hash表中的指定key是否存在 $redis->hexists('hash1', 'key1'); // true or false // hdel 删除hash表中指定key的元素 $redis->hdel('hash1', 'key2'); // true or false // hlen 返回hash表元素个数 $redis->hlen('hash1'); // 1 // hsetnx 增加一个元素,但不能重复 $redis->hsetnx('hash1', 'key1', 'v2'); // false $redis->hsetnx('hash1', 'key2', 'v2'); // true // hmset/hmget 存取多个元素到hash表 $redis->hmset('hash1', array( 'key3' => 'v3', 'key4' => 'v4' )); $redis->hmget('hash1', array('key3', 'key4')); // 返回相应的值 array('v3','v4') // hincrby 对指定key进行累加 $redis->hincrby('hash1', 'key5', 3); // 返回3 $redis->hincrby('hash1', 'key5', 10); // 返回13 // hkeys 返回hash表中的所有key $redis->hkeys('hash1'); // 返回array('key1','key2','key3','key4','key5') // hvals 返回hash表中的所有value $redis->hvals('hash1'); // 返回array('v1','v2','v3','v4',13) // hgetall 返回整个hash表元素 $redis->hgetall('hash1'); // 返回array('key1'=>'v1','key2'=>'v2','key3'=>'v3','key4'=>'v4','key5'=>13) /** * 排序操作 */ // sort 排序 $redis->rpush('tab', 3); $redis->rpush('tab', 2); $redis->rpush('tab', 17); $redis->sort('tab'); // 返回array(2,3,17) // 使用参数,可组合使用 array('sort' => 'desc','limit' => array(1, 2)) $redis->sort('tab', array('sort' => 'desc')); // 降序排列,返回array(17,3,2) $redis->sort('tab', array('limit' => array(1, 2))); // 返回顺序位置中1的元素2个(这里的2是指个数,而不是位置),返回array(3,17) $redis->sort('tab', array('limit' => array('alpha' => true))); // 按首字符排序返回array(17,2,3),因为17的首字符是'1'所以排首位置 $redis->sort('tab', array('limit' => array('store' => 'ordered'))); // 表示永久性排序,返回元素个数 $redis->sort('tab', array('limit' => array('get' => 'pre_*'))); // 使用了通配符'*'过滤元素,表示只返回以'pre_'开头的元素 /** * redis管理操作 */ // select 指定要操作的数据库 $redis->select('mydb'); // 指定为mydb,不存在则创建 // flushdb 清空当前库 $redis->flushdb(); // move 移动当库的元素到其它库 $redis->set('foo', 'bar'); $redis->move('foo', 'mydb2'); // 若'mydb2'库存在 // info 显示服务当状态信息 $redis->info(); // slaveof 配置从服务器 $redis->slaveof('127.0.0.1', 80); // 配置127.0.0.1端口80的服务器为从服务器 $redis->slaveof(); // 清除从服务器 // 同步保存服务器数据到磁盘 $redis->save(); // 异步保存服务器数据到磁盘 $redis->bgsave(); // ?? $redis->bgrewriteaof(); // 返回最后更新磁盘的时间 $redis->lastsave(); // set/get多个key-value $mkv = array( 'usr:0001' => 'First user', 'usr:0002' => 'Second user', 'usr:0003' => 'Third user' ); $redis->mset($mkv); // 存储多个key对应的value $retval = $redis->mget(array_keys($mkv)); // 获取多个key对应的value print_r($retval); // 批量操作 $replies = $redis->pipeline(function($pipe) { $pipe->ping(); $pipe->flushdb(); $pipe->incrby('counter', 10); // 增量操作 $pipe->incrby('counter', 30); $pipe->exists('counter'); $pipe->get('counter'); $pipe->mget('does_not_exist', 'counter'); }); print_r($replies); // CAS,事务性操作 function zpop($client, $zsetKey) { $element = null; $options = array( 'cas' => true, // Initialize with support for CAS operations 'watch' => $zsetKey, // Key that needs to be WATCHed to detect changes 'retry' => 3, // Number of retries on aborted transactions, after // which the client bails out with an exception. ); $txReply = $client->multiExec($options, function($tx) use ($zsetKey, &$element) { @list($element) = $tx->zrange($zsetKey, 0, 0); if (isset($element)) { $tx->multi(); // With CAS, MULTI *must* be explicitly invoked. $tx->zrem($zsetKey, $element); } }); return $element; } $zpopped = zpop($redis, 'zset'); echo isset($zpopped) ? "ZPOPed $zpopped" : "Nothing to ZPOP!", "\n"; // 对存取的key加前缀,如: 'nrk:' $redis->getProfile()->setPreprocessor(new KeyPrefixPreprocessor('nrk:')); // 分布式存储的一些方法 $multiple_servers = array( array( 'host' => '127.0.0.1', 'port' => 6379, 'database' => 15, 'alias' => 'first', ), array( 'host' => '127.0.0.1', 'port' => 6380, 'database' => 15, 'alias' => 'second', ), ); use Predis\Distribution\IDistributionStrategy; class NaiveDistributionStrategy implements IDistributionStrategy { private $_nodes, $_nodesCount; public function __construct() { $this->_nodes = array(); $this->_nodesCount = 0; } public function add($node, $weight = null) { $this->_nodes[] = $node; $this->_nodesCount++; } public function remove($node) { $this->_nodes = array_filter($this->_nodes, function($n) use ($node) { return $n !== $node; }); $this->_nodesCount = count($this->_nodes); } public function get($key) { $count = $this->_nodesCount; if ($count === 0) { throw new RuntimeException('No connections'); } return $this->_nodes[$count > 1 ? abs(crc32($key) % $count) : 0]; } public function generateKey($value) { return crc32($value); } } // 配置键分布策略 $options = array( 'key_distribution' => new NaiveDistributionStrategy(), ); $redis = new Predis\Client($multiple_servers, $options); for ($i = 0; $i < 100; $i++) { $redis->set("key:$i", str_pad($i, 4, '0', 0)); $redis->get("key:$i"); } $server1 = $redis->getClientFor('first')->info(); $server2 = $redis->getClientFor('second')->info(); printf("Server '%s' has %d keys while server '%s' has %d keys.\n", 'first', $server1['db15']['keys'], 'second', $server2['db15']['keys'] );
2025年08月20日
3 阅读
0 评论
0 点赞
2025-08-18
【PHP】bc系列函数(高精度数学运算)
PHP bc系列函数(高精度数学运算)简介:bc系列函数用于处理任意精度的十进制计算,适用于金融、科学计算等对精度要求极高的场景。需启用bcmath扩展(可通过phpinfo()检查是否启用)。特点:所有参数和返回值均为字符串类型(避免精度丢失)支持自定义小数位数(scale参数)提供基本运算、比较、幂运算等完整功能一、基础运算函数1. 加法:bcadd()// 语法:bcadd(左操作数, 右操作数, 保留小数位数) echo bcadd('123.456', '78.9', 2); // 202.36(123.456 + 78.9 = 202.356,保留2位小数) echo bcadd('100', '200', 0); // 300(整数相加)2. 减法:bcsub()// 语法:bcsub(被减数, 减数, 保留小数位数) echo bcsub('100.5', '30.25', 1); // 70.2(100.5 - 30.25 = 70.25,保留1位小数) echo bcsub('50', '75', 0); // -25(负数结果)3. 乘法:bcmul()// 语法:bcmul(乘数1, 乘数2, 保留小数位数) echo bcmul('2.5', '4.2', 2); // 10.50(2.5 × 4.2 = 10.5,保留2位小数) echo bcmul('100', '0.01', 0); // 1(整数与小数相乘)4. 除法:bcdiv()// 语法:bcdiv(被除数, 除数, 保留小数位数) echo bcdiv('10', '3', 3); // 3.333(10 ÷ 3 ≈ 3.3333...,保留3位) var_dump(bcdiv('5', '0', 0)); // false(除数为0时返回false)5. 取模(余数):bcmod()// 语法:bcmod(被除数, 除数) echo bcmod('10', '3'); // 1(10 ÷ 3 余数为1) echo bcmod('7.5', '2.5'); // 0.0(7.5 是 2.5 的整数倍)二、比较函数bccomp()// 语法:bccomp(值1, 值2, 比较精度) // 返回值:1(值1 > 值2)、0(相等)、-1(值1 < 值2) echo bccomp('10', '10', 0); // 0(相等) echo bccomp('10.5', '10.49', 1); // 1(10.5 > 10.49,精确到1位小数) echo bccomp('5.999', '6', 2); // -1(5.999 < 6,精确到2位小数)三、幂运算与开方1. 幂运算:bcpow()// 语法:bcpow(底数, 指数, 保留小数位数) echo bcpow('2', '3', 0); // 8(2³ = 8) echo bcpow('1.2', '2', 2); // 1.44(1.2² = 1.44)2. 幂运算取模:bcpowmod()// 语法:bcpowmod(底数, 指数, 模数, 保留小数位数) // 作用:(base^exponent) % modulus,比先算幂再取模更高效 echo bcpowmod('3', '3', '5', 0); // 2(3³=27,27%5=2)3. 开平方:bcsqrt()// 语法:bcsqrt(数值, 保留小数位数) echo bcsqrt('25', 0); // 5(√25 = 5) echo bcsqrt('2', 4); // 1.4142(√2 ≈ 1.4142,保留4位) var_dump(bcsqrt('-4', 0)); // false(负数无法开平方)四、全局配置函数bcscale()// 功能:设置所有bc函数的默认小数位数(全局生效) bcscale(2); // 全局默认保留2位小数 echo bcadd('1', '2'); // 3.00(使用全局配置) echo bcsub('5', '3.5'); // 1.50(自动补全2位小数) echo bcadd('1', '2', 0); // 3(局部参数优先级高于全局)关键注意事项参数类型:必须传入字符串(如'123.45'),而非数字(避免自动转换导致精度丢失)。返回值:始终为字符串,需转换类型时用(float)或(int)。扩展依赖:需在php.ini中启用extension=bcmath(重启服务生效)。精度优先级:函数的scale参数 > bcscale()全局配置。通过这些函数,PHP可以实现高精度计算,解决普通数值类型精度不足的问题。
2025年08月18日
4 阅读
0 评论
0 点赞
2025-07-30
【开发基础】nginx/postgresql/php/redis/ssdb的启动/关闭/重启命令及配置文件修改
以下是 Nginx、PostgreSQL、PHP、Redis 和 SSDB 在 Linux 系统下的常用管理命令及配置文件位置总结:1. Nginx启动/关闭/重启# 使用 systemd(CentOS 7+/Ubuntu 16.04+) systemctl start nginx # 启动 systemctl stop nginx # 停止 systemctl restart nginx # 重启 systemctl reload nginx # 重新加载配置 systemctl status nginx # 查看状态 # 使用 service(旧版系统) service nginx start|stop|restart|reload # 直接调用 nginx 命令(需 root 权限) nginx # 启动 nginx -s stop # 快速停止 nginx -s quit # 优雅停止 nginx -s reload # 重新加载配置配置文件主配置文件:/etc/nginx/nginx.conf站点配置目录:/etc/nginx/conf.d/ 或 /etc/nginx/sites-enabled/修改后检查配置并重启:nginx -t # 检查配置语法 systemctl reload nginx # 重新加载配置2. PostgreSQL启动/关闭/重启# 使用 systemd systemctl start postgresql-<版本号> # 启动(如:postgresql-14) systemctl stop postgresql-<版本号> # 停止 systemctl restart postgresql-<版本号> # 重启 systemctl status postgresql-<版本号> # 查看状态 # Ubuntu/Debian 通用命令 systemctl start|stop|restart postgresql # 手动控制(需切换到 postgres 用户) sudo -u postgres pg_ctl -D /var/lib/pgsql/<版本号>/data start|stop|restart配置文件主配置文件:/var/lib/pgsql/<版本号>/data/postgresql.conf认证配置:/var/lib/pgsql/<版本号>/data/pg_hba.conf修改后重启服务:systemctl restart postgresql-<版本号>3. PHP-FPM启动/关闭/重启# 使用 systemd(PHP-FPM 通常作为 FastCGI 进程管理器) systemctl start php-fpm # 启动(CentOS/RHEL) systemctl start php<版本号>-fpm # 启动(Ubuntu/Debian,如:php8.1-fpm) systemctl stop|restart|status php-fpm # 直接调用 php-fpm 命令 php-fpm -D # 后台启动 php-fpm -t # 检查配置语法 kill -USR2 `cat /run/php-fpm.pid` # 优雅重启配置文件主配置文件:/etc/php-fpm.conf 或 /etc/php/<版本号>/fpm/php-fpm.conf池配置目录:/etc/php-fpm.d/ 或 /etc/php/<版本号>/fpm/pool.d/PHP .ini 文件:/etc/php.ini 或 /etc/php/<版本号>/fpm/php.ini修改后重启服务:systemctl restart php-fpm4. Redis启动/关闭/重启# 使用 systemd systemctl start redis # 启动 systemctl stop redis # 停止 systemctl restart redis # 重启 systemctl status redis # 查看状态 # 手动启动(需指定配置文件) redis-server /etc/redis.conf # 前台启动 redis-server /etc/redis.conf & # 后台启动 # 关闭 Redis(通过客户端) redis-cli shutdown配置文件主配置文件:/etc/redis.conf修改后重启服务:systemctl restart redis5. SSDB启动/关闭/重启# 使用 systemd(需手动创建 service 文件) systemctl start ssdb # 启动 systemctl stop ssdb # 停止 systemctl restart ssdb # 重启 # 手动控制(通过 ssdb-server 命令) /opt/ssdb/ssdb-server /opt/ssdb/ssdb.conf -d # 后台启动 /opt/ssdb/ssdb-cli -h 127.0.0.1 -p 8888 shutdown # 关闭配置文件主配置文件:/opt/ssdb/ssdb.conf修改后重启服务:systemctl restart ssdb注意事项路径差异:不同 Linux 发行版或安装方式(源码/包管理器)可能导致配置文件路径不同,建议通过 whereis 或 find 命令查找。权限问题:操作服务时需确保用户有足够权限(如使用 sudo)。配置检查:修改配置文件后,建议先检查语法再重启服务。版本兼容性:部分命令可能因软件版本不同而略有差异。
2025年07月30日
12 阅读
0 评论
0 点赞
2025-07-30
【开发基础】macOS系统的常规使用方法
一、触控板使用技巧双指操作双指滑动:实现页面上下滚动双指开合:进行缩放操作双指左右滑动:在浏览器或桌面空间之间进行切换三指操作三指上推:显示所有打开的应用程序三指下滑:隐藏当前应用程序,显示桌面三指左右滑动:在全屏应用程序之间进行切换四指操作四指捏合:打开Launchpad(应用程序启动器)四指展开:显示应用程序坞二、Homebrew包管理器Homebrew是macOS系统上常用的包管理器,可用于安装各种软件。安装Homebrew打开终端,执行以下命令:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"常用brew命令brew install <package>:安装软件包brew uninstall <package>:卸载软件包brew upgrade <package>:升级软件包brew list:查看已安装的软件包brew search <package>:搜索软件包brew update:更新Homebrew自身brew doctor:检查Homebrew的状态三、macOS系统目录结构主要目录/Applications:这里存放的是用户安装的应用程序/System:包含系统文件,一般情况下不要对其进行修改/Users:用户主目录,例如/Users/your_username/Library:系统级别的资源库/usr:包含Unix工具和开发文件/tmp:临时文件目录,系统重启后该目录下的文件会被清除用户目录~/Documents:用于存放文档~/Downloads:下载文件的存放位置~/Desktop:桌面文件的存放处~/Pictures:图片文件的存放目录~/Movies:视频文件的存放目录~/Music:音乐文件的存放目录四、常用Linux命令(macOS基于Unix)文件操作ls:列出目录内容ls -l:以长格式显示文件和目录信息ls -a:显示包括隐藏文件在内的所有文件cd <directory>:切换到指定目录cd ..:返回上一级目录mkdir <directory>:创建新目录rm <file>:删除文件rm -r <directory>:递归删除目录及其内容cp <source> <destination>:复制文件或目录mv <source> <destination>:移动或重命名文件文件查看cat <file>:查看文件内容less <file>:分页查看文件内容head <file>:显示文件的开头部分tail <file>:显示文件的结尾部分权限管理chmod <permissions> <file>:修改文件权限chown <owner> <file>:修改文件所有者搜索grep <pattern> <file>:在文件中搜索指定模式find <directory> -name <pattern>:在目录中查找文件系统信息uname -a:显示系统信息df -h:查看磁盘使用情况free -h:查看内存使用情况top:显示正在运行的进程htop:一个更高级的进程查看器(需要先使用brew安装)网络ping <host>:测试与主机的连接ifconfig:显示网络接口信息curl <url>:下载URL内容wget <url>:下载文件(需要先使用brew安装)五、终端快捷键光标移动Ctrl + A:将光标移动到行首Ctrl + E:将光标移动到行尾Ctrl + F:光标向前移动一个字符Ctrl + B:光标向后移动一个字符历史命令Up Arrow:查看上一条命令Down Arrow:查看下一条命令Ctrl + R:搜索历史命令其他Ctrl + L:清屏Ctrl + C:中断当前命令Ctrl + D:退出终端会话六、其他实用技巧截图Command + Shift + 3:截取整个屏幕Command + Shift + 4:截取屏幕的一部分Command + Shift + 5:打开截图工具(可录制屏幕)快速操作Command + Tab:在应用程序之间进行切换Command + Space:打开Spotlight搜索Option + Click:在应用程序坞中查看应用程序的所有窗口Command + H:隐藏当前应用程序Command + Q:退出当前应用程序
2025年07月30日
6 阅读
0 评论
0 点赞
2025-07-26
【PHP】ThinkPHP8数据库迁移示例
以下是一个包含 各种字段类型、约束条件、索引和特殊配置 的ThinkPHP8数据库迁移示例(以products产品表为例),覆盖常见场景:迁移文件示例(database/migrations/20250726153000_create_products_table.php)<?php use think\migration\Migrator; use think\migration\db\Column; class CreateProductsTable extends Migrator { /** * 执行迁移(创建表结构) */ public function change() { // 创建products表,配置表级参数 $table = $this->table('products', [ 'engine' => 'InnoDB', // 数据库引擎 'charset' => 'utf8mb4', // 字符集 'collation' => 'utf8mb4_unicode_ci', // 排序规则 'comment' => '产品表(包含多种字段类型示例)', // 表注释 'auto_increment' => 1000, // 自增ID起始值(MySQL支持) ]); // 字段定义(覆盖各种场景) $table // 1. 自增主键(整数型,无符号,自增) ->addColumn('id', 'integer', [ 'identity' => true, // 自增 'unsigned' => true, // 无符号(只存正数) 'comment' => '产品ID(主键)' ]) // 2. 字符串(有限长度,非空,唯一索引) ->addColumn('sku', 'string', [ 'limit' => 50, // 长度限制 'null' => false, // 非空(默认) 'comment' => '产品SKU(唯一标识)', 'unique' => true // 唯一约束 ]) // 3. 字符串(长文本,可空,带默认值) ->addColumn('name', 'string', [ 'limit' => 255, 'null' => false, 'default' => '', // 默认空字符串 'comment' => '产品名称' ]) // 4. 文本类型(无长度限制,可空) ->addColumn('description', 'text', [ 'null' => true, // 允许为空 'comment' => '产品详细描述' ]) // 5. 小数类型(高精度,带默认值) ->addColumn('price', 'decimal', [ 'precision' => 10, // 总位数 'scale' => 2, // 小数位数 'default' => 0.00, // 默认0.00 'comment' => '产品售价' ]) // 6. 整数类型(无符号,默认值,索引) ->addColumn('stock', 'integer', [ 'unsigned' => true, // 无符号(只存正数) 'default' => 0, // 默认库存0 'comment' => '库存数量', 'index' => true // 普通索引 ]) // 7. 枚举类型(固定可选值) ->addColumn('status', 'enum', [ 'values' => ['draft', 'active', 'disabled'], // 可选值 'default' => 'draft', // 默认草稿状态 'comment' => '产品状态(draft:草稿, active:上架, disabled:下架)' ]) // 8. 布尔类型(tinyint,默认true) ->addColumn('is_recommend', 'boolean', [ 'default' => true, // 默认true(1) 'comment' => '是否推荐(1:是, 0:否)' ]) // 9. 外键关联(整数,无符号,级联操作) ->addColumn('category_id', 'integer', [ 'unsigned' => true, 'null' => false, 'comment' => '所属分类ID', 'index' => true // 普通索引 ]) // 10. JSON类型(存储复杂结构,可空) ->addColumn('tags', 'json', [ 'null' => true, 'comment' => '产品标签(JSON格式,如["新品","热卖"])' ]) // 11. 时间戳类型(自动维护) ->addTimestamps() // 自动添加 create_time 和 update_time(datetime类型) // 12. 软删除字段(自动维护删除时间) ->addSoftDelete() // 添加 delete_time 字段(软删除标识) // 13. 整数类型(排序权重,默认0) ->addColumn('sort', 'integer', [ 'default' => 0, 'comment' => '排序权重(值越大越靠前)' ]) // 添加外键约束(关联分类表) ->addForeignKey('category_id', 'categories', 'id', [ 'delete' => 'CASCADE', // 分类删除时,关联产品也删除 'update' => 'CASCADE' // 分类ID更新时,产品关联ID同步更新 ]) // 添加联合索引(名称+状态) ->addIndex(['name', 'status'], [ 'name' => 'idx_name_status' // 自定义索引名 ]) // 执行创建表 ->create(); } /** * 回滚迁移(删除表) */ public function down() { // 先删除外键约束(避免删除表失败) $this->table('products')->dropForeignKey('category_id'); // 再删除表 $this->dropTable('products'); } }关键字段配置说明字段名类型/配置核心特点id自增整数、主键无符号,自增起始值1000sku字符串、唯一约束唯一标识,非空,长度50name字符串、默认值非空,默认空字符串,用于产品名称description文本类型、可空无长度限制,允许为空(适合长文本描述)price小数(10,2)高精度价格,默认0.00stock无符号整数、索引库存数量,非负,普通索引提升查询效率status枚举类型固定可选值,默认草稿状态is_recommend布尔类型本质tinyint,默认true(1)category_id外键、级联操作关联分类表,删除/更新时级联处理tagsJSON类型存储数组/对象结构,适合非结构化数据create_time时间戳(自动维护)由addTimestamps()生成,记录创建时间delete_time软删除字段由addSoftDelete()生成,非NULL表示已删除执行迁移# 创建表 php think migrate:run # 回滚(删除表) php think migrate:rollback注意事项外键依赖:示例中category_id关联categories表,需确保categories表已存在(可先创建分类表迁移)。索引优化:根据查询场景添加索引,避免过度索引影响写入性能。软删除:addSoftDelete()会自动添加delete_time字段(datetime类型,默认NULL),适合逻辑删除。枚举值:修改枚举值需谨慎,生产环境可能需要先添加新值再迁移数据。字段注释:每个字段添加清晰注释,便于后期维护(尤其团队协作场景)。通过这个示例,可以覆盖ThinkPHP8迁移工具中绝大多数字段配置场景,实际开发中可根据需求调整。
2025年07月26日
11 阅读
0 评论
0 点赞
2025-07-26
【PHP】ThinkPHP8 数据库迁移与数据填充完全指南
ThinkPHP8 数据库迁移与数据填充完全指南(官方文档补充版)一、安装与配置1. 安装迁移扩展composer require topthink/think-migration2. 配置文件说明配置文件位于 config/database.php默认迁移表名:think_migration支持多数据库连接3. 环境要求PHP >= 8.1ThinkPHP >= 8.0建议使用 InnoDB 引擎(支持事务)二、迁移文件操作1. 创建迁移文件# 创建基础迁移文件 php think migrate:create CreateUsersTable # 创建带表名的迁移文件(自动生成基础结构) php think migrate:create AddEmailToUsersTable --table=users # 指定数据库连接 php think migrate:create CreateLogsTable --connection=log2. 迁移文件结构生成的迁移文件位于 database/migrations,示例:<?php use think\migration\Migrator; use think\migration\db\Column; class CreateUsersTable extends Migrator { /** * 执行迁移(向上操作) */ public function up() { $table = $this->table('users'); $table->addColumn('name', 'string', ['limit' => 50]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addTimestamps() // 自动添加 create_time 和 update_time 字段 ->create(); } /** * 回滚迁移(向下操作) */ public function down() { $this->dropTable('users'); } }三、常用字段类型字段类型描述示例用法integer整数类型->addColumn('age', 'integer')biginteger大整数类型->addColumn('user_id', 'biginteger')string字符串类型->addColumn('name', 'string', ['limit' => 50])text长文本类型->addColumn('content', 'text')datetime日期时间类型->addColumn('created_at', 'datetime')timestamp时间戳类型->addColumn('updated_at', 'timestamp')boolean布尔类型->addColumn('status', 'boolean', ['default' => 0])decimal高精度小数->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])enum枚举类型->addColumn('gender', 'enum', ['values' => ['male', 'female', 'other']])四、表结构操作1. 创建表public function up() { $table = $this->table('users', [ 'engine' => 'InnoDB', 'charset' => 'utf8mb4', 'comment' => '用户表', 'collation' => 'utf8mb4_unicode_ci' ]); $table->addColumn('id', 'integer', ['identity' => true]) // 自增ID(默认主键) ->addColumn('username', 'string', ['limit' => 30]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addColumn('status', 'boolean', ['default' => 1]) ->addIndex(['username']) // 普通索引 ->addUniqueIndex(['email']) // 唯一索引 ->addTimestamps() // 自动添加 create_time 和 update_time ->addSoftDelete() // 添加 delete_time 软删除字段 ->create(); }2. 修改表添加字段:public function up() { $this->table('users') ->addColumn('phone', 'string', ['limit' => 20, 'after' => 'email']) ->update(); }修改字段:public function up() { $this->table('users') ->changeColumn('phone', 'string', ['limit' => 11, 'default' => '']) ->update(); }删除字段:public function up() { $this->table('users') ->removeColumn('phone') ->update(); }3. 添加外键约束public function up() { $this->table('posts') ->addColumn('user_id', 'integer') ->addColumn('title', 'string') ->addColumn('content', 'text') ->addForeignKey('user_id', 'users', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) ->update(); }五、执行迁移命令1. 执行所有未迁移的文件php think migrate:run2. 回滚上一次迁移php think migrate:rollback3. 回滚到指定版本php think migrate:rollback --target=20250726101500 # 指定时间戳4. 重置所有迁移(先回滚再执行)php think migrate:reset5. 刷新数据库(重置并重新执行所有迁移)php think migrate:refresh6. 查看迁移状态php think migrate:status7. 指定数据库连接php think migrate:run --connection=db_log # 指定日志数据库六、数据填充操作1. 创建数据填充文件php think seed:create Users # 创建Users表的数据填充器2. 编写数据填充逻辑<?php use think\migration\Seeder; use think\facade\Db; class Users extends Seeder { /** * 填充数据 */ public function run() { $data = []; for ($i = 1; $i <= 10; $i++) { $data[] = [ 'username' => 'user' . $i, 'email' => 'user' . $i . '@example.com', 'password' => password_hash('123456', PASSWORD_DEFAULT), 'create_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s') ]; } // 使用批量插入提高性能 Db::name('users')->insertAll($data); } }3. 执行数据填充php think seed:run # 执行所有填充器 php think seed:run --seeder Users # 执行指定填充器七、高级技巧1. 使用 change() 方法(简化双向操作)public function change() { $table = $this->table('users'); // 创建表 $table->addColumn('username', 'string') ->addColumn('email', 'string') ->create(); // 修改表 $table->addColumn('phone', 'string') ->update(); }2. 使用事务public function up() { $this->getAdapter()->beginTransaction(); try { $this->table('table1')->addColumn(...)->create(); $this->table('table2')->addColumn(...)->create(); $this->getAdapter()->commit(); } catch (\Exception $e) { $this->getAdapter()->rollBack(); throw $e; } }3. 创建数据库快照php think migrate:snapshot # 创建当前数据库结构快照4. 指定迁移文件路径php think migrate:run --path=database/migrations/custom # 指定自定义路径八、最佳实践1. 命名规范迁移文件:YYYYMMDDHHMMSS_表名_操作.php(自动生成)表名:使用小写字母和下划线(如 user_info)字段名:使用小写字母和下划线(如 create_time)2. 避免复杂SQL单个迁移文件只做单一变更避免在迁移中执行数据迁移操作3. 测试迁移在开发环境充分测试使用测试数据库验证回滚功能4. 生产环境注意事项迁移前备份数据库使用 --pretend 参数预览变更避免在高峰期执行大型迁移5. 团队协作迁移文件提交到版本控制系统避免多人同时修改同一迁移文件拉取代码后先执行 php think migrate:run九、常见问题与解决方案1. 迁移文件冲突问题:多个迁移文件时间戳相近导致执行顺序异常解决方案:使用 --timestamp 参数手动指定时间戳php think migrate:create NewTable --timestamp=202507261530002. 外键约束错误问题:删除表时外键约束阻止操作解决方案:在 down() 方法中先删除外键public function down() { $this->table('posts') ->dropForeignKey('user_id') ->update(); $this->dropTable('posts'); }3. 数据填充重复问题问题:多次执行填充器导致数据重复解决方案:在填充前清空表public function run() { Db::name('users')->delete(true); // 清空表 // 填充新数据 }4. 迁移性能问题问题:大型表迁移缓慢解决方案:分批次执行数据迁移使用数据库原生工具导入大型数据避免在迁移中使用复杂查询
2025年07月26日
14 阅读
0 评论
0 点赞
2025-07-16
【Nginx】Nginx需具体配置示例(涵盖了复杂场景下的高级应用)
以下是 Nginx 常见场景的具体配置示例,配置文件可存放在 /etc/nginx/conf.d/ 目录下(Linux)或直接修改 nginx.conf(Windows):一、基础配置:静态网站托管配置文件(/etc/nginx/conf.d/mysite.conf)server { listen 80; server_name example.com www.example.com; # 替换为你的域名 # 网站根目录 root /var/www/html; index index.html index.htm; # 日志配置 access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # 静态文件缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 30d; # 静态资源缓存30天 } # 错误页面 error_page 404 /404.html; error_page 500 502 503 504 /50x.html; }操作步骤创建网站目录:sudo mkdir -p /var/www/html添加测试文件:echo "Hello, Nginx!" > /var/www/html/index.html验证配置:nginx -t重载配置:nginx -s reload二、反向代理:转发到后端服务场景1:代理到本地 Node.js 应用(端口3000)server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:3000; # 后端服务地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } }场景2:代理到多个后端服务器(负载均衡)# 定义后端服务器组 upstream backend_servers { server backend1.example.com weight=5; # 权重5 server backend2.example.com weight=3; # 权重3 server backend3.example.com backup; # 备份服务器 } server { listen 80; server_name example.com; location / { proxy_pass http://backend_servers; # 其他 proxy_set_header 配置同上 } }三、HTTPS 配置(使用 Let's Encrypt 证书)前提条件已安装 Certbot:sudo apt install certbot python3-certbot-nginx域名已解析到服务器 IP自动配置(推荐)sudo certbot --nginx -d example.com -d www.example.comCertbot 会自动修改 Nginx 配置并设置证书自动更新。手动配置示例server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; # 强制 HTTPS } server { listen 443 ssl http2; server_name example.com www.example.com; # 证书路径(Let's Encrypt 默认路径) ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 优化配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 其他配置(如 root、location 等)同 HTTP 配置 }四、URL 重写与跳转示例1:www 到非 www 跳转server { listen 80; server_name www.example.com; return 301 https://example.com$request_uri; }示例2:路径重写location /old-path/ { rewrite ^/old-path/(.*)$ /new-path/$1 permanent; # 301 永久重定向 }示例3:基于参数跳转if ($args ~* "id=([0-9]+)") { return 301 https://example.com/item/$1; }五、限流配置(防爬/防刷)限制单个 IP 的请求频率# http 块中定义限流规则 http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server { listen 80; server_name example.com; location /api/ { limit_req zone=mylimit burst=20 nodelay; # 每秒最多10个请求,突发最多20个,超出的请求直接返回503 } } }六、WebSocket 代理支持配置示例server { listen 80; server_name ws.example.com; location /ws/ { proxy_pass http://backend_websocket_server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; # 防止 WebSocket 连接超时 proxy_read_timeout 86400; } }七、多站点配置(虚拟主机)在同一服务器托管多个域名# 站点1 server { listen 80; server_name site1.com www.site1.com; root /var/www/site1; } # 站点2 server { listen 80; server_name site2.com www.site2.com; root /var/www/site2; }八、静态文件优化配置配置 Gzip 压缩http { gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_buffers 16 8k; gzip_http_version 1.1; }九、常见问题处理配置1. 解决跨域请求(CORS)location /api/ { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always; # OPTIONS 请求直接返回 204 if ($request_method = 'OPTIONS') { return 204; } }2. 隐藏 Nginx 版本信息http { server_tokens off; # 隐藏响应头中的 Nginx 版本 }配置验证与重载每次修改配置后,务必执行:nginx -t # 验证配置语法 nginx -s reload # 重载配置(不中断服务)以上配置可根据实际需求组合使用,例如同时配置 HTTPS、反向代理和限流。如需更复杂的场景(如 A/B 测试、灰度发布),可进一步扩展配置。以下是 Nginx 更高级的扩展配置示例,适用于复杂场景:十、A/B 测试与灰度发布基于 Cookie 的流量分发http { # 定义变量存储分流规则 map $cookie_ab_test $backend { default backend_a; "b" backend_b; } upstream backend_a { server backend1.example.com; } upstream backend_b { server backend2.example.com; } server { listen 80; server_name example.com; # 首次访问设置 Cookie if ($cookie_ab_test = "") { set $ab_test $random % 2; # 随机分为两组 if ($ab_test = 0) { add_header Set-Cookie "ab_test=a; path=/; domain=.example.com"; } if ($ab_test = 1) { add_header Set-Cookie "ab_test=b; path=/; domain=.example.com"; } } location / { proxy_pass http://$backend; } } }十一、基于用户区域的流量路由根据 IP 地理位置分发请求# 需要安装 ngx_http_geoip2_module 模块 http { geoip2 /path/to/GeoLite2-Country.mmdb { $geoip2_data_country_code country iso_code; } map $geoip2_data_country_code $backend { default backend_global; "CN" backend_china; "US" backend_usa; } upstream backend_global { server global.example.com; } upstream backend_china { server china.example.com; } upstream backend_usa { server usa.example.com; } server { listen 80; server_name example.com; location / { proxy_pass http://$backend; } } }十二、内容缓存配置配置代理缓存(如缓存 API 响应)http { # 定义缓存参数 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; server { listen 80; server_name api.example.com; location /static-api/ { proxy_pass http://backend_api; proxy_cache my_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 302 12h; # 成功响应缓存12小时 proxy_cache_valid 404 1m; # 404缓存1分钟 # 缓存命中时添加响应头,便于调试 add_header X-Cache $upstream_cache_status; } } }十三、健康检查与自动剔除故障节点配置主动健康检查(需 nginx-plus 或 lua 模块)http { upstream backend_servers { server backend1.example.com max_fails=3 fail_timeout=30s; server backend2.example.com max_fails=3 fail_timeout=30s; # 健康检查(Nginx Plus 功能) zone backend 64k; health_check interval=5s fails=3 passes=2 uri=/health; } server { listen 80; server_name example.com; location / { proxy_pass http://backend_servers; } } }使用 Lua 模块实现健康检查(开源版)# 需要安装 ngx_lua 模块 http { lua_shared_dict healthcheck 1m; init_by_lua_block { local hc = require "resty.healthcheck" local checker = hc.new({ name = "backend_checker", shm_name = "healthcheck", type = "http", http_req = "GET /health HTTP/1.0\r\nHost: backend\r\n\r\n", interval = 2000, # 2秒检查一次 timeout = 1000, # 超时时间1秒 fall = 3, # 连续3次失败标记为不可用 rise = 2, # 连续2次成功标记为可用 }) -- 添加后端服务器 checker:add_target("backend1.example.com", nil, 80, true) checker:add_target("backend2.example.com", nil, 80, true) -- 启动后台检查 local ok, err = checker:start() if not ok then ngx.log(ngx.ERR, "failed to start health checker: ", err) return end } upstream backend_servers { server backend1.example.com; server backend2.example.com; # 使用 Lua 脚本判断服务器是否健康 server 0.0.0.1; # 防止 upstream 为空 balancer_by_lua_block { local hc = require "resty.healthcheck" local checker = hc.new({ name = "backend_checker", shm_name = "healthcheck", type = "http", }) local ok, err = checker:get_reachable() if not ok then ngx.log(ngx.ERR, "no reachable backend server: ", err) return ngx.exit(500) end -- 设置要代理的服务器 local host, port = ok.host, ok.port local ok, err = ngx.balancer.set_current_peer(host, port) if not ok then ngx.log(ngx.ERR, "failed to set current peer: ", err) return ngx.exit(500) end } } }十四、请求限流与熔断基于漏桶算法的限流http { # 定义漏桶限流规则 limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s; # 每秒5个请求 server { listen 80; server_name api.example.com; location /api/ { limit_req zone=api_limit burst=10 nodelay; # 突发10个请求 # 超过限流时返回自定义错误页面 error_page 503 @api_blocked; } location @api_blocked { default_type application/json; return 503 '{"error":"Too many requests","code":429}'; } } }熔断机制(当后端服务不可用时)upstream backend_servers { server backend1.example.com; server backend2.example.com; # 当后端服务器返回500、502、503、504时,将请求转发到其他服务器 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; }十五、WebSocket 集群与会话保持基于 IP Hash 的会话保持upstream websocket_backend { ip_hash; # 相同IP的请求始终转发到同一后端服务器 server backend1.example.com:8080; server backend2.example.com:8080; } server { listen 80; server_name ws.example.com; location /ws/ { proxy_pass http://websocket_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; # 保持长连接 proxy_read_timeout 3600s; } }十六、自定义日志格式与分析JSON 格式日志http { log_format json_log '{ "timestamp": "$time_iso8601", "remote_addr": "$remote_addr", "host": "$host", "request": "$request", "status": $status, "body_bytes_sent": $body_bytes_sent, "request_time": $request_time, "upstream_response_time": "$upstream_response_time", "http_referer": "$http_referer", "http_user_agent": "$http_user_agent" }'; server { listen 80; server_name example.com; access_log /var/log/nginx/access_json.log json_log; } }十七、HTTP/2 与 TLS 优化配置HTTP/2 与 OCSP 装订server { listen 443 ssl http2; server_name example.com; # 证书配置 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # 启用 OCSP 装订 ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # TLS 优化 ssl_protocols TLSv1.3 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"; # HTTP/2 优化 http2_push_preload on; # 启用 HTTP/2 预推送 }十八、自定义错误页面配置全局错误页面http { # 定义错误页面路径 error_page 400 /errors/400.html; error_page 401 /errors/401.html; error_page 403 /errors/403.html; error_page 404 /errors/404.html; error_page 500 502 503 504 /errors/50x.html; server { listen 80; server_name example.com; location /errors/ { internal; # 只允许内部重定向访问 root /var/www/html; } } }十九、Nginx 与 Lua 脚本集成示例:请求签名验证# 需要安装 ngx_lua 模块 server { listen 80; server_name api.example.com; location /api/ { # 验证请求签名 access_by_lua_block { local secret_key = "your_secret_key" local request_time = ngx.var.arg_timestamp local signature = ngx.var.arg_signature # 检查时间戳是否过期(防止重放攻击) local current_time = os.time() if not request_time or math.abs(current_time - tonumber(request_time)) > 300 then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid timestamp"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end # 验证签名 local args = ngx.req.get_uri_args() local sorted_keys = {} for k, _ in pairs(args) do if k ~= "sign" then table.insert(sorted_keys, k) end end table.sort(sorted_keys) local sign_str = "" for _, k in ipairs(sorted_keys) do sign_str = sign_str .. k .. "=" .. args[k] .. "&" end sign_str = sign_str .. "key=" .. secret_key local resty_sha1 = require "resty.sha1" local str = require "resty.string" local sha1 = resty_sha1:new() sha1:update(sign_str) local digest = sha1:final() local calculated_sign = str.to_hex(digest) if calculated_sign ~= signature then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid signature"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end } proxy_pass http://backend_api; } }二十、基于 JWT 的认证JWT 验证示例# 需要安装 lua-resty-jwt 模块 server { listen 80; server_name auth.example.com; location /api/ { access_by_lua_block { local jwt = require "resty.jwt" local secret = "your_jwt_secret" -- 从请求头中获取 JWT local auth_header = ngx.var.http_Authorization if not auth_header then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Authorization header missing"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 提取 token local _, _, token = string.find(auth_header, "Bearer%s+(.+)") if not token then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"Invalid token format"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 验证 JWT local jwt_obj = jwt:verify(secret, token) if not jwt_obj.verified then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say('{"error":"' .. jwt_obj.reason .. '"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 将用户信息传递给后端 ngx.req.set_header("X-User-ID", jwt_obj.payload.user_id) ngx.req.set_header("X-User-Role", jwt_obj.payload.role) } proxy_pass http://backend_api; } }配置验证与重载每次修改配置后,务必执行:nginx -t # 验证配置语法 nginx -s reload # 重载配置(不中断服务)以上配置涵盖了复杂场景下的高级应用,可根据实际需求组合使用。对于生产环境,建议先在测试环境验证配置,确保稳定性。
2025年07月16日
8 阅读
0 评论
0 点赞
2025-07-16
【Nginx】Nginx安装与运行
以下是在不同操作系统上安装和运行 Nginx 的详细步骤:一、Ubuntu/Debian 系统1. 安装 Nginx# 更新包索引 sudo apt update # 安装 Nginx sudo apt install nginx # 验证安装 nginx -v # 输出类似: nginx version: nginx/1.18.02. 控制 Nginx 服务# 启动服务 sudo systemctl start nginx # 设置开机自启 sudo systemctl enable nginx # 检查服务状态 sudo systemctl status nginx # 停止服务 sudo systemctl stop nginx # 重启服务(修改配置后常用) sudo systemctl restart nginx # 重新加载配置(不中断现有连接) sudo systemctl reload nginx3. 防火墙配置# 允许 HTTP 和 HTTPS 流量 sudo ufw allow 'Nginx HTTP' # 仅允许 80 端口 sudo ufw allow 'Nginx HTTPS' # 仅允许 443 端口 sudo ufw allow 'Nginx Full' # 同时允许 80 和 443 端口二、CentOS/RHEL 系统1. 安装 Nginx# 添加 Nginx 官方仓库 sudo tee /etc/yum.repos.d/nginx.repo <<EOF [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/ gpgcheck=0 enabled=1 EOF # 安装 Nginx sudo yum install nginx # 验证安装 nginx -v2. 控制 Nginx 服务# 启动服务 sudo systemctl start nginx # 设置开机自启 sudo systemctl enable nginx # 其他命令(与 Ubuntu 相同) sudo systemctl {stop|restart|reload|status} nginx3. 防火墙配置# 允许 HTTP 和 HTTPS 流量 sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload三、macOS(使用 Homebrew)1. 安装 Homebrew(如未安装)/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"2. 安装和管理 Nginx# 安装 Nginx brew install nginx # 启动服务 brew services start nginx # 后台运行并开机自启 # 或临时启动(不设开机自启) nginx # 停止服务 brew services stop nginx # 或直接杀进程 killall nginx # 配置文件位置 /usr/local/etc/nginx/nginx.conf # 访问测试 http://localhost:8080 # macOS 默认监听 8080 端口四、Windows 系统1. 下载与安装访问 Nginx 官网下载页面,下载 Windows 版本(.zip 文件)。解压到指定目录(如 C:\nginx)。2. 控制 Nginx# 进入 Nginx 目录 cd C:\nginx # 启动 Nginx start nginx.exe # 停止 Nginx nginx.exe -s stop # 快速停止 # 或 nginx.exe -s quit # 优雅停止 # 重新加载配置 nginx.exe -s reload # 验证配置文件语法 nginx.exe -t3. 访问测试打开浏览器访问 http://localhost,若看到 "Welcome to nginx!" 页面,则安装成功。五、验证安装是否成功浏览器访问: 打开浏览器,输入 http://服务器IP地址(或 http://localhost),若看到 Nginx 欢迎页面,则安装成功。检查进程:ps -ef | grep nginx # 应看到 master 和 worker 进程端口监听:netstat -tulpn | grep :80 # 检查 80 端口是否被监听六、配置文件位置Linux:/etc/nginx/nginx.conf(主配置),/etc/nginx/conf.d/(站点配置)macOS(Homebrew):/usr/local/etc/nginx/nginx.confWindows:C:\nginx\conf\nginx.conf七、常见问题端口冲突:若 80 端口被占用,修改 nginx.conf 中的 listen 指令(如改为 8080)。权限问题:Linux 下若无法监听 80/443 端口,可使用 sudo 或调整 SELinux/AppArmor 配置。配置语法错误:修改配置后,使用 nginx -t 检查语法,再 nginx -s reload 重新加载。八、下一步配置虚拟主机:创建多个网站配置文件(.conf)放在 /etc/nginx/conf.d/ 目录下。配置 HTTPS:使用 Let's Encrypt 免费证书实现 HTTPS。负载均衡:配置 upstream 模块将请求分发到多个后端服务器。
2025年07月16日
15 阅读
0 评论
0 点赞
2025-07-16
【Nginx】什么是反向代理?
反向代理(Reverse Proxy)是一种服务器端代理技术,用于接收客户端的请求并将其转发到后端的多个目标服务器,然后将目标服务器的响应返回给客户端。与正向代理(如VPN)不同,反向代理对客户端是透明的,客户端无需感知代理服务器的存在,直接与反向代理服务器通信。核心概念与原理工作流程客户端向反向代理服务器(如Nginx、Apache)发送请求(如访问https://example.com)。反向代理服务器根据配置规则(如URL路径、请求头)将请求转发给后端的目标服务器(如Tomcat、Node.js应用)。目标服务器处理请求并返回响应给反向代理服务器。反向代理服务器将响应返回给客户端,客户端认为直接与目标服务器通信。关键特点隐藏后端服务器:客户端只能看到反向代理服务器的IP地址,无法直接访问后端服务器,增强安全性。统一入口:反向代理作为所有请求的唯一入口,简化了客户端与后端的交互。负载均衡:可将请求分发到多个后端服务器,平衡负载,提高可用性。反向代理的主要作用安全防护屏蔽后端服务器的真实IP,防止外部直接攻击(如DDOS、端口扫描)。集中进行访问控制(如IP黑名单、请求过滤)和安全加固(如WAF功能)。负载均衡将请求按策略(如轮询、IP哈希、响应时间)分发到多个后端服务器,避免单点故障,提升系统吞吐量。结合健康检查机制,自动剔除故障节点,保证服务可用性。性能优化缓存静态内容:缓存HTML、CSS、JS、图片等静态资源,减少后端服务器压力。压缩与优化:对响应内容进行Gzip压缩、HTTP/2升级等,加速数据传输。SSL/TLS卸载:由反向代理统一处理HTTPS加密和解密,减轻后端服务器计算负担。流量控制与路由根据请求特征(如URL、用户区域)将流量导向不同的后端服务(如A/B测试、灰度发布)。实现跨域请求(CORS)处理、请求限流(如限制IP访问频率)等。协议转换与兼容性将HTTP请求转换为其他协议(如WebSocket、gRPC),适配不同后端服务。处理不同API版本的路由,支持向后兼容。典型应用场景大型网站与应用如Google、Facebook等通过反向代理分发全球流量,隐藏内部架构。微服务架构作为API网关,统一管理微服务的访问,实现认证、限流、日志等公共逻辑。容器化与云原生在Kubernetes集群中,通过Ingress Controller(基于Nginx、Traefik等)实现服务暴露和流量路由。企业内网应用将内部系统(如OA、CRM)通过反向代理暴露到公网,同时保证安全性。对比正向代理与反向代理特性正向代理反向代理代理对象代理客户端(用户)代理服务器(服务端)客户端感知需要手动配置(如浏览器代理)透明,客户端无感知主要用途突破限制、隐藏客户端IP负载均衡、安全防护、性能优化典型场景VPN、翻墙工具Nginx、Apache、CDN节点技术选型常见的反向代理软件包括:Nginx:高性能、轻量,适合高并发场景,支持HTTP/2、WebSocket等。Apache HTTP Server:功能全面,适合复杂配置和模块化扩展。HAProxy:专注于负载均衡,支持TCP和HTTP协议。Traefik:专为微服务和容器环境设计,自动发现后端服务。Envoy:云原生架构中的高性能代理,支持动态配置和可观测性。总结反向代理是现代分布式系统的核心组件,通过隐藏后端架构、负载均衡、安全防护和性能优化,显著提升了系统的可用性、可扩展性和安全性。无论是企业级应用还是互联网产品,反向代理都是构建高并发、高可靠服务的关键技术之一。
2025年07月16日
5 阅读
0 评论
0 点赞
2025-05-22
【MySQL】查询百万数据导出处理方法(数据集合统一返回)
public function getExportList($map) { // 定义初始分页大小和最小分页大小 $initialPageSize = 1000; $minPageSize = 100; $pageSize = $initialPageSize; // 计算可用内存(保留15%缓冲) $memoryLimit = (int) ini_get('memory_limit') * 0.85; $lastId = 0; $allData = []; $hasMoreData = true; // 用于动态调整分页大小的变量 $lastBatchMemoryUsage = 0; $avgRecordSize = 0; while ($hasMoreData) { // 动态调整分页大小 if ($lastBatchMemoryUsage > 0 && $avgRecordSize > 0) { $currentMemory = memory_get_usage(true) / (1024 * 1024); $availableMemory = $memoryLimit - $currentMemory; // 基于剩余内存和记录大小计算安全分页大小 $safePageSize = max( $minPageSize, min( $initialPageSize, (int)($availableMemory / ($avgRecordSize * 1.5)) // 1.5倍安全系数 ) ); $pageSize = ($safePageSize < $pageSize) ? $safePageSize : $pageSize; } // 执行分页查询 $list = self::where($map) ->withoutField(['z_create_time', 'z_update_time']) ->where('z_id', '>', $lastId) ->order('z_id', 'asc') ->limit($pageSize) ->select(); if ($list->isEmpty()) { break; } $listData = $list->toArray(); $batchCount = count($listData); // 计算当前批次内存占用 $batchMemory = memory_get_usage(true); foreach ($listData as $record) { $allData[] = $record; // 直接添加减少内存操作 } $batchMemory = (memory_get_usage(true) - $batchMemory) / (1024 * 1024); // 更新内存跟踪指标 if ($batchCount > 0) { $currentAvg = $batchMemory / $batchCount; $avgRecordSize = ($avgRecordSize > 0) ? ($avgRecordSize * 0.7 + $currentAvg * 0.3) // 平滑平均 : $currentAvg; $lastBatchMemoryUsage = $batchMemory; } // 更新迭代变量 $hasMoreData = ($batchCount === $pageSize); if ($hasMoreData) { $lastRecord = end($listData); $lastId = (int)$lastRecord['z_id']; reset($listData); // 重置数组指针 } // 资源清理 unset($list, $listData); if (gc_enabled()) { gc_collect_cycles(); } } return $allData; } //导出14730条数据,需要34.05秒,字段有50个,全是非空状态,字段数量会影响响应时间,一般导出起飞无压力 public function getExportList($map) { $pageSize = 1000; $lastId = 0; $allData = []; while (true) { // 查询当前批次 $list = self::where($map) ->withoutField(['z_create_time', 'z_update_time']) ->where('z_id', '>', $lastId) ->order('z_id', 'asc') ->limit($pageSize) ->select(); // 无数据时终止循环 if ($list->isEmpty()) { break; } $batch = $list->toArray(); $batchSize = count($batch); // 直接追加数据(避免array_merge内存峰值) foreach ($batch as $record) { $allData[] = $record; } // 到达数据末尾 if ($batchSize < $pageSize) { break; } // 更新最后ID $lastId = (int)end($batch)['z_id']; // 资源清理 unset($list, $batch); if ($batchSize >= 500) { // 仅在大批次后回收 gc_collect_cycles(); } } return $allData; } //这个是34.59秒。其他有很多方法,正在尝试30秒内
2025年05月22日
16 阅读
0 评论
1 点赞
2025-05-17
【ThinkPHP】创建有背景的微信小程序二维码
<?php namespace app\common\lib\wechat; use think\Exception; use think\facade\Cache; use think\facade\Request; use think\Image; use think\facade\Db; class QrCode extends Basic { // 小程序页面路径 private string $path = "pages/myCenter/invite"; // 小程序码保存路径 private string $savePath = "./storage/qrcode/"; // 二维码参数 private string $param = ""; public function __construct(string $path = "", string $field = "", string $param = "", int $width = 430) { if (!empty($path) && empty($field)) { $this->path = $path; } if (!empty($path) && !empty($field) && !empty($param)) { $this->path = "{$path}?{$field}={$param}"; } $this->param = $param; } /** * 生成小程序码并可选择合并背景图 * * @param string $backgroundPath 背景图路径 * @param int $qrWidth 二维码在背景图上的宽度位置 * @param int $qrHeight 二维码在背景图上的高度位置 * @return string 生成后的二维码URL */ public function setQrcode(string $backgroundPath = '', int $qrWidth = 160, int $qrHeight = 530): string { try { // 获取访问令牌 $accessToken = Cache::get('accesstoken'); if (empty($accessToken)) { $accessToken = (new AccessToken())->getAccesToken(); Cache::set('accesstoken', $accessToken, 7200); // 假设token有效期为2小时 } // 请求小程序码 $url = "https://api.weixin.qq.com/wxa/getwxacode?access_token={$accessToken}"; $data = [ 'path' => $this->path, 'scene' => 'type=qrcode', 'width' => 430, ]; $result = $this->curlPost($url, $data, 'POST'); if ($result === false || isset($result['errcode'])) { throw new Exception("Failed to get QR code: " . json_encode($result)); } // 保存二维码到文件 $fileName = md5($this->param); $directory = $this->savePath . date('Ymd') . '/'; if (!$this->createDirectory($directory)) { throw new Exception("目录创建失败: {$directory}"); } $filePath = "{$directory}{$fileName}.png"; if (!file_put_contents($filePath, $result)) { throw new Exception("文件写入失败: {$filePath}"); } // 生成完整URL $fullDomain = (new \app\common\lib\data\Str())->getFullDomain(); $qrcodeUrl = $fullDomain . ltrim($filePath, '.'); // 合并背景图(如果提供) if (!empty($backgroundPath)) { return $this->mergeWithBackground($backgroundPath, $filePath, $qrcodeUrl, $qrWidth, $qrHeight); } return $qrcodeUrl; } catch (Exception $e) { // 记录错误日志 error_log($e->getMessage()); return ''; } } /** * 将二维码与背景图合并 */ private function mergeWithBackground(string $backgroundPath, string $qrcodePath, string $defaultUrl, int $qrWidth, int $qrHeight): string { try { $fileName = md5(uniqid() . time()); $newImagePath = $this->savePath . date('Ymd') . "/{$fileName}.png"; $background = Image::open($backgroundPath); $background->water($qrcodePath, [$qrWidth, $qrHeight])->save($newImagePath); $imageInfo = [ 'url' => Request::domain() . substr($newImagePath, 1), 'size' => filesize($newImagePath), 'name' => $fileName, 'mime' => mime_content_type($newImagePath), 'ext' => 'png', ]; $fileData = [ 'f_uuid' => setUUID(), 'f_file' => $imageInfo['url'], 'f_location' => 0, 'f_type' => 'image', 'f_info' => serialize($imageInfo), 'f_user_uuid' => $this->request->index_user_uuid ?? '', 'f_create_time' => time(), ]; Db::name('File')->insert($fileData); return $imageInfo['url']; } catch (Exception $e) { // 记录错误日志 error_log($e->getMessage()); return $defaultUrl; } } /** * 递归创建目录 */ private function createDirectory(string $path, int $mode = 0777, bool $recursive = true): bool { if (is_dir($path)) { return true; } if (mkdir($path, $mode, $recursive)) { chmod($path, $mode); return true; } if (!is_dir(dirname($path))) { if ($this->createDirectory(dirname($path), $mode, $recursive)) { return $this->createDirectory($path, $mode, $recursive); } } return false; } /** * 发送HTTP POST请求 */ // public function curlPost($url, $data, $method = "POST") // { // $ch = curl_init(); //1.初始化 // curl_setopt($ch, CURLOPT_URL, $url); //2.请求地址 // curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);//3.请求方式 // //4.参数如下 // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//https // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); // curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');//模拟浏览器 // curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // curl_setopt($ch, CURLOPT_AUTOREFERER, 1); // curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept-Encoding: gzip, deflate'));//gzip解压内容 // curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // // if ($method == "POST") {//5.post方式的时候添加数据 // $data = json_encode($data); // curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // } // curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // $tmpInfo = curl_exec($ch);//6.执行 // // if (curl_errno($ch)) {//7.如果出错 // return curl_error($ch); // } // curl_close($ch);//8.关闭 // return $tmpInfo; // } }
2025年05月17日
25 阅读
0 评论
0 点赞
2025-05-13
【PHP】PHPExcel 使用手册
PHPExcel 是一个用于操作 Microsoft Excel 格式文件的 PHP 库,它支持读取和写入多种格式(如 .xls, .xlsx, .csv 等)。不过需要注意,PHPExcel 项目已停止维护,推荐使用其继任者 PhpSpreadsheet。以下是 PHPExcel 的基本使用指南:1. 安装 PHPExcel# 使用 Composer 安装(推荐) composer require phpoffice/phpexcel2. 读取 Excel 文件require 'vendor/autoload.php'; use PHPExcel_IOFactory; // 读取文件 $inputFileName = 'example.xlsx'; $objPHPExcel = PHPExcel_IOFactory::load($inputFileName); // 获取第一个工作表 $sheet = $objPHPExcel->getActiveSheet(); // 获取最高行和最高列 $highestRow = $sheet->getHighestRow(); $highestColumn = $sheet->getHighestColumn(); // 遍历数据 for ($row = 1; $row <= $highestRow; $row++) { for ($col = 'A'; $col <= $highestColumn; $col++) { $cellValue = $sheet->getCell($col.$row)->getValue(); echo "Row $row, Column $col: $cellValue\n"; } }3. 创建新的 Excel 文件require 'vendor/autoload.php'; use PHPExcel; use PHPExcel_IOFactory; // 创建新的 PHPExcel 对象 $objPHPExcel = new PHPExcel(); // 设置文档属性 $objPHPExcel->getProperties() ->setCreator("Your Name") ->setTitle("Sample Excel File"); // 获取活动工作表 $sheet = $objPHPExcel->getActiveSheet(); // 设置单元格值 $sheet->setCellValue('A1', 'Hello'); $sheet->setCellValue('B1', 'World!'); $sheet->setCellValue('A2', 'This is a sample spreadsheet.'); // 保存为 .xlsx 文件 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('new_file.xlsx');4. 设置样式// 设置字体样式 $sheet->getStyle('A1')->getFont() ->setName('Arial') ->setSize(14) ->setBold(true) ->setColor(new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_RED)); // 设置单元格背景色 $sheet->getStyle('A1')->getFill() ->setFillType(PHPExcel_Style_Fill::FILL_SOLID) ->getStartColor()->setARGB('FFEEEEEE'); // 设置对齐方式 $sheet->getStyle('A1')->getAlignment() ->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER) ->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); // 设置列宽 $sheet->getColumnDimension('A')->setWidth(20);5. 合并单元格// 合并 A1 到 B1 的单元格 $sheet->mergeCells('A1:B1'); // 取消合并 $sheet->unmergeCells('A1:B1');6. 处理日期格式// 设置日期值(PHPExcel 使用 Excel 时间戳格式) $dateTime = new DateTime(); $sheet->setCellValue('A1', $dateTime->format('Y-m-d H:i:s')); // 设置单元格格式为日期 $sheet->getStyle('A1')->getNumberFormat() ->setFormatCode('yyyy-mm-dd hh:mm:ss');7. 保存为不同格式// 保存为 .xlsx 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('file.xlsx'); // 保存为 .xls 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save('file.xls'); // 保存为 CSV 格式 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'CSV'); $objWriter->save('file.csv');8. 从浏览器下载文件// 设置响应头 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="download.xlsx"'); header('Cache-Control: max-age=0'); // 输出到浏览器 $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('php://output'); exit;注意事项性能问题:处理大量数据时,PHPExcel 可能占用较多内存。考虑使用 PHPExcel_CachedObjectStorageFactory 进行优化。替代方案:推荐使用 PhpSpreadsheet,它是 PHPExcel 的官方继任者,修复了许多问题并提供更好的性能。内存优化:// 设置为只读模式以减少内存消耗 $objReader = PHPExcel_IOFactory::createReader('Excel2007'); $objReader->setReadDataOnly(true); $objPHPExcel = $objReader->load('large_file.xlsx');如需更详细的文档,请参考 PHPExcel 官方文档(注意:项目已停止维护)。
2025年05月13日
24 阅读
0 评论
0 点赞
2025-05-08
毕业设计,程序启航 —— 为大学生打造专属编程毕业设计服务
前言大学尾声,毕业设计是每位学子迈向专业领域的重要里程碑。对于计算机相关专业的学生而言,编程毕业设计更是展现技术实力、创新思维与实践能力的核心舞台。然而,面对从网页设计到前后端开发等复杂多样的项目需求,许多同学往往感到无从下手。别担心!我们提供一站式编程毕业设计服务,助你轻松开启毕业设计之旅,为未来职业生涯筑牢根基。一、网页设计:打造数字世界的视觉盛宴网页是互联网时代的关键窗口,优质的网页设计既能吸引用户,又能高效传递信息、彰显创意。我们深谙网页设计的重要性,在毕业设计中为你提供全方位服务:创意规划定制:基于你的毕业设计主题,我们将与你深度沟通,挖掘独特创意点。无论是企业官网、个人作品展示,还是特定功能专题页面,我们都会量身定制整体规划。从页面布局、色彩搭配,到导航结构、交互元素,每个细节都贴合设计需求,兼顾用户体验与视觉美感。专业视觉塑造:专业设计团队运用前沿理念与工具,精心雕琢网页视觉效果。从高清图片处理、精美图标设计,到流畅动画效果、和谐色彩搭配,力求让网页在视觉上脱颖而出,为毕业设计增添亮眼亮点。响应式完美适配:在多设备浏览的当下,响应式设计至关重要。我们确保网页在桌面电脑、笔记本、平板电脑及手机等各类屏幕尺寸上,都能呈现一致且优质的浏览体验,体现你对技术细节的全面把控,提升毕业设计专业性。二、前端代码编写:构建交互流畅的用户界面前端代码是实现网页功能与交互的关键,直接影响用户体验。我们的前端开发服务聚焦于打造高效、稳定且具创意的前端代码:精准技术选型:依据毕业设计项目需求,选择 HTML5、CSS3、JavaScript 等适配的前端技术栈,并结合 Vue.js、React.js 等流行框架,快速搭建稳固架构。这些框架可提升开发效率,保障代码的可维护性与扩展性,增强毕业设计技术竞争力。交互功能实现:我们以用户体验为核心,精心设计并实现各类交互功能。无论是表单验证、动态数据展示,还是拖拽、滑动、折叠等复杂交互效果,都能通过精湛代码实现流畅自然的交互体验,让网页从静态展示升级为智能互动界面,为毕业设计注入趣味性与实用性。性能深度优化:开发过程中,我们高度关注网页性能。通过代码压缩、图片优化、缓存策略等技术手段,确保网页加载迅速、运行流畅,展示你对技术细节与性能优化的重视,助力毕业设计脱颖而出。三、后台程序编写:筑牢数据处理与业务逻辑的基石后台程序是毕业设计的核心,承担数据存储、业务逻辑处理及前后端交互等关键任务。我们提供专业后台程序编写服务,保障毕业设计稳定高效运行:数据库设计优化:根据毕业设计业务需求,精心设计合理数据库结构,选用 MySQL、MongoDB 等适配的数据库管理系统,并进行性能优化。从数据表创建、索引建立,到数据操作与复杂查询优化,全方位保障数据库高效存储与处理数据,为毕业设计提供坚实数据支撑。业务逻辑实现:深入理解你的设计需求,将复杂业务逻辑转化为清晰高效的后台代码。无论是用户管理、订单处理,还是数据分析等业务功能,都以严谨编程实现准确可靠的功能。同时注重代码可读性与可维护性,遵循编程规范,使毕业设计技术实现更专业规范,便于后续扩展维护。接口开发与安全防护:在前后端分离模式下,开发稳定高效的 API 接口,确保数据交互顺畅安全。采用身份验证、数据加密、访问控制等技术手段,防止数据泄露与恶意攻击,守护毕业设计数据与系统安全。四、全程辅导与技术支持:伴你成长,助你成功我们明白,毕业设计不仅是代码编写,更是学习成长的过程。为此,我们提供全程辅导与技术支持:一对一专属辅导:专业导师将提供一对一辅导服务,从选题到答辩全程跟踪进度。针对技术难题、设计思路优化等问题,根据你的实际情况定制个性化辅导方案,助力顺利完成毕业设计。丰富学习资源:为帮助你提升技术能力,我们提供丰富技术培训与学习资源,涵盖编程语言基础教程、前端框架实战课程、后台开发高级技巧等。让你在完成毕业设计的同时,积累宝贵技术经验,为职业发展奠基。科学项目管理:制定详细项目计划,将毕业设计分解为具体任务并明确时间节点。实施过程中定期沟通,跟踪进度,及时解决问题,确保按时高质量完成。同时根据实际灵活调整计划,保障毕业设计顺利有序推进。五、联系方式如需咨询或下单,欢迎通过 QQ 3584685883 与我们私聊,我们随时在线为你服务。六、结语毕业设计是大学生涯的重要里程碑,也是迈向专业领域的关键一步。选择我们的编程毕业设计服务,你将获得专业的网页设计、前端开发、后台程序编写及全程辅导支持。我们致力于打造高质量、有创意且技术先进的毕业设计作品,助你在众多作品中脱颖而出,为职业生涯开启成功之门。让我们携手同行,共同书写编程毕业设计的精彩篇章!
2025年05月08日
32 阅读
0 评论
0 点赞
2025-05-07
【PHP】PHP实现无限级树形分类 树形分类 如果子集的pid不是空,父级id不存在,则子集不展示
/** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/12/17 22:27 * @param $arr * @param $id * @param $pid * @return array * @Description:树形分类 如果子集的pid不是空,父级id不存在,则子集不展示 */ public function getTreeByDateDelChile($arr,$id,$pid) { $items = []; foreach($arr as $v){ $items[$v[$id]] = $v; } $tree = []; foreach($items as $k => $item){ if($item[$pid] &&!isset($items[$item[$pid]])){ unset($items[$k]); continue; } if(isset($items[$item[$pid]])){ $items[$item[$pid]]['child'][] = &$items[$k]; }else{ $tree[] = &$items[$k]; } } return $tree; } /** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/9/14 16:59 * @param array $arr * @param string $children * @return array * @Description:将树形数组转换成一维数组 */ public function setManyTosingle(array $arr = [], string $children = 'child'): array { $result = []; foreach ($arr as $item) { // 先将当前节点添加到结果数组中 $newItem = $item; if (isset($newItem[$children])) { unset($newItem[$children]); } $result[] = $newItem; // 递归处理子节点 if (isset($item[$children]) && is_array($item[$children])) { $result = array_merge($result, $this->setManyTosingle($item[$children], $children)); } } return $result; }
2025年05月07日
27 阅读
0 评论
0 点赞
2025-05-06
【PHP】清空MySQL数据,索引重置
<?php // 定义排除表的常量 const EXCLUDE_TABLES = [ 'web_admin_company', 'web_admin_func', 'web_admin_role', 'web_admin_user', 'web_app_config', 'web_china_city', 'web_china_city_area', 'web_china_city_backup', 'web_company_config', 'web_shop_category', 'web_tppay', // 'web_shop_goods_tmpserver', ]; class SomeClass { public function truncateTables() { try { // 开启事务 Db::startTrans(); // 获取所有表名 $tables = Db::query('SHOW TABLES'); $tableNames = array_map('current', $tables); foreach ($tableNames as $tableName) { if (!in_array($tableName, EXCLUDE_TABLES)) { // 使用参数化查询清空表并重置索引 Db::execute("TRUNCATE TABLE `$tableName`"); echo "表 {$tableName} 已清空<br>"; } } // 提交事务 Db::commit(); } catch (\Exception $e) { // 回滚事务 Db::rollback(); // 记录错误日志 error_log("发生错误: " . $e->getMessage()); echo "发生错误: " . $e->getMessage(); } } }
2025年05月06日
24 阅读
0 评论
0 点赞
2025-04-29
【Nginx】什么是Nginx
Nginx(发音为“engine x”)是一款高性能的开源Web服务器、反向代理服务器,同时也支持HTTP缓存、负载均衡、邮件代理等功能。它由俄罗斯工程师Igor Sysoev于2004年开发,旨在解决高并发场景下的性能问题,2011年成立Nginx公司(现属于F5 Networks),目前广泛应用于全球大型网站和复杂分布式系统中。核心功能与特点高性能与高并发处理采用事件驱动的异步非阻塞模型(Epoll/Kqueue),能高效处理海量并发连接(单台服务器可支持数万并发),内存占用低,适合高流量场景。对比传统服务器(如Apache),在静态资源处理和反向代理场景下性能优势显著。反向代理与负载均衡反向代理:接收客户端请求,转发至后端多个服务器(如Tomcat、Node.js、Django等),隐藏后端架构细节,提升安全性。负载均衡:支持轮询、加权轮询、IP哈希、最少连接数等策略,将流量均匀分配到后端服务器,避免单点压力。HTTP缓存与静态资源处理内置缓存机制(如Proxy Cache),可缓存静态文件(HTML、CSS、JS、图片等),减少后端服务器压力,加速客户端响应。直接高效处理静态资源,无需依赖额外模块。丰富的功能扩展支持HTTPS(SSL/TLS加密)、URL重写、Gzip压缩、跨域请求(CORS)、限流(如限制IP访问频率)等。可通过插件(如Lua脚本、Nginx Plus模块)实现自定义逻辑,满足复杂业务需求。轻量与灵活配置文件简洁(nginx.conf),支持热加载(无需重启服务即可更新配置)。资源消耗低,适合运行在容器(Docker)或资源受限的环境中。典型应用场景Web服务器:直接托管静态资源,或通过FastCGI协议处理动态请求(如PHP、Python应用)。反向代理服务器:作为前端入口,转发请求到后端多个应用服务器,实现高可用性和扩展性。负载均衡器:在分布式系统中分发流量,避免单节点过载,提升整体吞吐量。API网关:在微服务架构中作为统一入口,处理路由、认证、限流等逻辑。缓存服务器:通过缓存静态或动态内容,降低后端压力,优化用户访问速度。知名应用案例国内外大型网站:Google、Facebook、Twitter、京东、淘宝、新浪、网易、知乎等均使用Nginx作为核心服务器组件。云计算与CDN:AWS、阿里云、腾讯云等云服务商的CDN节点大量部署Nginx,用于边缘节点的内容分发。总结Nginx以高性能、低资源消耗、高可扩展性成为现代Web架构的核心组件,尤其适合高并发、分布式系统和微服务场景。无论是作为独立Web服务器,还是与其他技术(如Tomcat、Kubernetes)结合使用,都能有效提升系统的稳定性和性能。
2025年04月29日
26 阅读
0 评论
0 点赞
2025-04-23
【PHP】管家婆各个大类的基础调用数据方法
<?php namespace app\common\lib\gjp\warehouse; use think\Exception; class Warehouse { protected $dbName; public function __construct() { $this->dbName = "*****"; // } //查询单据 public function getWarehouse($paramkey = "", $paramJson = "", $userId = "***********")// { $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $md5BeforeStr = "apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'query'."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."paramjson".$paramJson."paramkey".$paramkey."serviceid".$serviceid.$gjpSignInfo['SignKey']; $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'paramkey' => (string)$paramkey, 'paramjson' => (string)($paramJson), 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"query", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); if($result['code'] != 0){ print_r($result);die; } return $result; } //生产单据 public function setReceipts($vchtype = 0,$billdata = '',$userId = "*************"){ $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $md5BeforeStr = "apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'process'."billdata". $billdata ."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."processtype"."0"."serviceid".$serviceid."vchcode"."0"."vchtype".$vchtype.$gjpSignInfo['SignKey']; $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'processtype' => (integer)0, 'vchtype' => (integer)$vchtype, 'vchcode' => (integer)0, 'billdata' => (string)$billdata, 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"process", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; // print_r($data);die; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); return $result; } //新增或修改 基础资料新增 public function businessBaseInfo($basetype = "", $baseinfodata = "", $rec = "", $type = 1, $userId = "*********"){ if($type == 1){ $typeName = "add"; }else{ $typeName = "modify"; } if(empty($rec)){ (string)$rec = "0"; } $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $gjpSignInfo = $instanceGjpBasic->getSignKey(); $instanceGjpApiUrl = new \app\common\lib\gjp\Apiurl($userId); $gjpApiUrlInfo = $instanceGjpApiUrl -> getApiUrl(); // print_r($gjpApiUrlInfo);die; $mobile = empty($gjpApiUrlInfo['GraspCloudMobile']) ? 0 : $gjpApiUrlInfo['GraspCloudMobile']; $serviceid = empty($gjpApiUrlInfo['GraspCloudServerId']) ? 0 : $gjpApiUrlInfo['GraspCloudServerId']; $requestUrl = $gjpApiUrlInfo['ApiServerAddress']; $md5BeforeStr ="actiontype".$typeName."apiparam".$gjpApiUrlInfo['ApiParam']."apitype".'baseinfo'."baseinfodata".$baseinfodata."basetype".$basetype."dbname".(string)$this->dbName."interiorapi"."1"."managename"."GraspCMServerApi.dll"."mobile".$mobile."rec".$rec."serviceid".$serviceid.$gjpSignInfo['SignKey']; // "actiontype".$typeName."apiparam".$gjpApiUrlInfo['ApiParam']."apitype"."baseinfo"."basetype".$basetype."baseinfodata".$baseinfodata."dbname".$this->dbName."interiorapi".1."managename"."GraspCMServerApi.dll"."mobile".$mobile."serviceid".$serviceid."sign".md5($md5BeforeStr) $data = [ 'managename' => (string)"GraspCMServerApi.dll", 'dbname' => (string)$this->dbName, 'actiontype' => (string)$typeName, 'rec' => (string)$rec, 'basetype' => (string)$basetype, 'baseinfodata' => (string)$baseinfodata, 'apiparam' => (string)$gjpApiUrlInfo['ApiParam'], 'apitype' => (string)"baseinfo", 'sign' => (string)md5($md5BeforeStr), 'mobile' => (string)$mobile, 'serviceid' => (string)$serviceid, 'interiorapi' => (integer)1, ]; $encodedParams = http_build_query($data); $result = $instanceGjpBasic->curlRequest($requestUrl,"POST",$encodedParams,true,false); if($result['code'] != 0){ print_r($result);die; } return $result; } }
2025年04月23日
28 阅读
0 评论
0 点赞
2025-04-23
【PHP】获取程序Api地址 - 管家婆基础对接 PHP版本
<?php namespace app\common\lib\gjp; class Apiurl { protected $userId; protected $appKey; protected $SercretKey; protected $currentTimestamp; protected $dbName; public function __construct($userId = '********') { $this->appKey = "*****************"; $this->SercretKey = "*******************"; $this->currentTimestamp = time(); $this->userId = $userId; $this->dbName = '*****'; } public function getApiUrl() { $url = "http://api.cmgrasp.com/CMGraspApi/GateWay"; $instanceGjpBasic = new \app\common\lib\gjp\Basic(); $instanceStr = new \app\common\lib\data\Str(); $randamStr = $instanceStr->setNonce(32,true); $md5BeforeStr = "AppKey".$this->appKey."InvalidTime".date('YmdHis',$this->currentTimestamp)."RandamStr".$randamStr."UserId".$this->userId.$this->SercretKey; $data = [ 'MethodName' => (string)"graspcm.cmapi.getcustomerapiurl", 'AppKey' => (string)$this->appKey, 'UserId' => (string)$this->userId, 'InvalidTime' => (string)date('Y-m-d H:i:s'), 'RandamStr' => (string)$randamStr, 'SignStr' => (string)md5($md5BeforeStr), 'DbName' => (string)$this->dbName ]; $jsonData = json_encode($data); $result = $instanceGjpBasic->curlRequest($url,"POST",$jsonData,true,false); if($result['RetCode'] != 0){ $this->getApiUrl(); } $resultData = json_decode($result['RetMsg'],true); return $resultData; } }
2025年04月23日
24 阅读
0 评论
0 点赞
2025-04-23
【PHP】获取接口所需的SignKey - 管家婆基础对接 PHP版本
<?php namespace app\common\lib\gjp; use think\Exception; class Basic { protected $appKey; protected $SercretKey; protected $currentTimestamp; public function __construct() { $this->appKey = "*************"; $this->SercretKey = "*************"; $this->currentTimestamp = time(); } public function getSignKey() { if(!empty( cache('gjp_sign'))) { $dataResult = cache('gjp_sign'); return $dataResult; } $signUrl = "http://api.cmgrasp.com/CMGraspApi/GateWay"; $instanceStr = new \app\common\lib\data\Str(); $randamStr = $instanceStr->setNonce(32,true); $md5BeforeStr = "AppKey".$this->appKey."InvalidTime".date('YmdHis',$this->currentTimestamp)."RandamStr".$randamStr.$this->SercretKey; $data = [ "MethodName" => (string)"graspcm.cmapi.getsignstr", 'AppKey' => (string)$this->appKey, 'InvalidTime' => (string)date('Y-m-d H:i:s',$this->currentTimestamp), 'RandamStr' => (string)$randamStr, 'SignStr' => (string)md5($md5BeforeStr), ]; $jsonData = json_encode($data); $result = $this->curlRequest($signUrl,"POST", $jsonData,true,false); if($result['RetCode'] != 0) { $this->getSignKey(); } $dataResult = json_decode($result['RetMsg'],true); if(empty( cache('gjp_sign'))) { $dataResult = json_decode($result['RetMsg'],true); cache('gjp_sign',$dataResult,60*60*20); } return $dataResult; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/4/1 10:39 * @param string $url url地址 * @param string $method 请求方法,默认为 'GET',可选值为 'GET' 或 'POST' * @param mixed $data 要发送的数据,如果是 POST 请求则为数据内容,否则为 null * @param array $headers 自定义请求头信息 * @param int $timeout 超时时间,默认为 30 秒 * @param bool $verifySSL 是否验证 SSL 证书,默认为 true * @param bool $flbg 返回值是否转成数组,默认不转 * @param bool $headercontent 是否获取请求的header值内容,默认不获取 * @return array|bool|mixed|string * @Description:curl请求 */ public function curlRequest($url, $method = 'GET', $data = null, $flbg = false, $verifySSL = true, $headers = [], $headerContent = false, $timeout = 30) { // 初始化 cURL 会话 $ch = curl_init(); // 设置要请求的 URL curl_setopt($ch, CURLOPT_URL, $url); // 设置获取的信息以字符串形式返回,而不是直接输出 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 设置超时时间 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // 设置请求方法 if ($method === 'POST') { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } // 设置请求头 if (!empty($headers)) { curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } // 设置是否验证 SSL 证书 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySSL); // 执行 cURL 会话并获取响应 $response = curl_exec($ch); // 获取 HTTP 响应码 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 如果 cURL 执行出错 if (curl_errno($ch)) { // 输出错误信息 echo 'Curl error: ' . curl_error($ch); // 关闭 cURL 会话并返回 false curl_close($ch); return false; } // 如果 HTTP 响应码大于等于 400(表示错误) elseif ($httpCode >= 400) { // 输出错误信息 echo "HTTP error: $httpCode"; // 关闭 cURL 会话并返回 false curl_close($ch); return false; } // 处理是否获取请求头内容 if ($headerContent && $httpCode == 200) { $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = substr($response, 0, $headerSize); $body = substr($response, $headerSize); curl_close($ch); return [$headers, $body]; } // 关闭 cURL 会话 curl_close($ch); // 处理是否将响应转换为数组 if ($flbg) { $response = json_decode($response, true); } // 返回响应内容 return $response; } }
2025年04月23日
21 阅读
0 评论
0 点赞
2025-04-23
精准检测,助力社交管理 —— 微信好友检测服务来袭!
精准检测,助力社交管理 —— 微信好友检测服务来袭!不发消息也能查!微信好友删除 / 拉黑检测服务上线。不用再群发 “测试消息” 尴尬试探!我们的静默检测技术,0 打扰标注两大核心状态 ——「删除我的人」「拉黑我的人」,5000 好友状态 1 次搞定,社交管理从此精准避雷!
2025年04月23日
1,299 阅读
0 评论
59 点赞
1
2
...
13