首页
关于小站
朋友
壁纸
留言
时光之书
笔顺字帖
LayUI手册
Search
1
【PHP】PHPoffice/PHPSpreadsheet读取和写入Excel
1,673 阅读
2
【Layui】控制页面元素展示隐藏
1,520 阅读
3
【Git】No tracked branch configured for branch master or the branch doesn't exist.
1,460 阅读
4
【PHP】PHP实现JWT生成和验证
1,370 阅读
5
精准检测,助力社交管理 —— 微信好友检测服务来袭!
1,278 阅读
默认分类
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
设计模式
Git
排序算法
基础
小破孩
累计撰写
244
篇文章
累计收到
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手册
搜索到
240
篇与
的结果
2022-06-23
【RabbitMQ】RabbitMQ实现消息队列的完整代码
前言为什么使用RabbitMq而不是ActiveMq或者RocketMq?首先,从业务上来讲,我并不要求消息的100%接受率,并且,我需要结合php开发,RabbitMq相较RocketMq,延迟较低(微妙级)。至于ActiveMq,貌似问题较多。RabbitMq对各种语言的支持较好,所以选择RabbitMq。先安装PHP对应的RabbitMQ,这里用的是 php_amqp 不同的扩展实现方式会有细微的差异.php扩展地址: http://pecl.php.net/package/amqp具体以官网为准 http://www.rabbitmq.com/getstarted.html介绍config.php 配置信息BaseMQ.php MQ基类ProductMQ.php 生产者类ConsumerMQ.php 消费者类Consumer2MQ.php 消费者2(可有多个)Config.php <?php return [ //配置 'host' => [ 'host' => '127.0.0.1', 'port' => '5672', 'login' => 'guest', 'password' => 'guest', 'vhost'=>'/', ], //交换机 'exchange'=>'word', //路由 'routes' => [], ];BaseMQ.php<?php /** * Created by PhpStorm. * User: pc * Date: 2019/07/13 * Time: 14:11 */ namespace MyObjSummary\rabbitMQ; /** Member * AMQPChannel * AMQPConnection * AMQPEnvelope * AMQPExchange * AMQPQueue * Class BaseMQ * @package MyObjSummary\rabbitMQ */ class BaseMQ { /** MQ Channel * @var \AMQPChannel */ public $AMQPChannel ; /** MQ Link * @var \AMQPConnection */ public $AMQPConnection ; /** MQ Envelope * @var \AMQPEnvelope */ public $AMQPEnvelope ; /** MQ Exchange * @var \AMQPExchange */ public $AMQPExchange ; /** MQ Queue * @var \AMQPQueue */ public $AMQPQueue ; /** conf * @var */ public $conf ; /** exchange * @var */ public $exchange ; /** link * BaseMQ constructor. * @throws \AMQPConnectionException */ public function __construct() { $conf = require 'config.php' ; if(!$conf) throw new \AMQPConnectionException('config error!'); $this->conf = $conf['host'] ; $this->exchange = $conf['exchange'] ; $this->AMQPConnection = new \AMQPConnection($this->conf); if (!$this->AMQPConnection->connect()) throw new \AMQPConnectionException("Cannot connect to the broker!\n"); } /** * close link */ public function close() { $this->AMQPConnection->disconnect(); } /** Channel * @return \AMQPChannel * @throws \AMQPConnectionException */ public function channel() { if(!$this->AMQPChannel) { $this->AMQPChannel = new \AMQPChannel($this->AMQPConnection); } return $this->AMQPChannel; } /** Exchange * @return \AMQPExchange * @throws \AMQPConnectionException * @throws \AMQPExchangeException */ public function exchange() { if(!$this->AMQPExchange) { $this->AMQPExchange = new \AMQPExchange($this->channel()); $this->AMQPExchange->setName($this->exchange); } return $this->AMQPExchange ; } /** queue * @return \AMQPQueue * @throws \AMQPConnectionException * @throws \AMQPQueueException */ public function queue() { if(!$this->AMQPQueue) { $this->AMQPQueue = new \AMQPQueue($this->channel()); } return $this->AMQPQueue ; } /** Envelope * @return \AMQPEnvelope */ public function envelope() { if(!$this->AMQPEnvelope) { $this->AMQPEnvelope = new \AMQPEnvelope(); } return $this->AMQPEnvelope; } } ProductMQ.php <?php //生产者 P namespace MyObjSummary\rabbitMQ; require 'BaseMQ.php'; class ProductMQ extends BaseMQ { private $routes = ['hello','word']; //路由key /** * ProductMQ constructor. * @throws \AMQPConnectionException */ public function __construct() { parent::__construct(); } /** 只控制发送成功 不接受消费者是否收到 * @throws \AMQPChannelException * @throws \AMQPConnectionException * @throws \AMQPExchangeException */ public function run() { //频道 $channel = $this->channel(); //创建交换机对象 $ex = $this->exchange(); //消息内容 $message = 'product message '.rand(1,99999); //开始事务 $channel->startTransaction(); $sendEd = true ; foreach ($this->routes as $route) { $sendEd = $ex->publish($message, $route) ; echo "Send Message:".$sendEd."\n"; } if(!$sendEd) { $channel->rollbackTransaction(); } $channel->commitTransaction(); //提交事务 $this->close(); die ; } } try{ (new ProductMQ())->run(); }catch (\Exception $exception){ var_dump($exception->getMessage()) ; }ConsumerMQ.php <?php //消费者 C namespace MyObjSummary\rabbitMQ; require 'BaseMQ.php'; class ConsumerMQ extends BaseMQ { private $q_name = 'hello'; //队列名 private $route = 'hello'; //路由key /** * ConsumerMQ constructor. * @throws \AMQPConnectionException */ public function __construct() { parent::__construct(); } /** 接受消息 如果终止 重连时会有消息 * @throws \AMQPChannelException * @throws \AMQPConnectionException * @throws \AMQPExchangeException * @throws \AMQPQueueException */ public function run() { //创建交换机 $ex = $this->exchange(); $ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型 $ex->setFlags(AMQP_DURABLE); //持久化 //echo "Exchange Status:".$ex->declare()."\n"; //创建队列 $q = $this->queue(); //var_dump($q->declare());exit(); $q->setName($this->q_name); $q->setFlags(AMQP_DURABLE); //持久化 //echo "Message Total:".$q->declareQueue()."\n"; //绑定交换机与队列,并指定路由键 echo 'Queue Bind: '.$q->bind($this->exchange, $this->route)."\n"; //阻塞模式接收消息 echo "Message:\n"; while(True){ $q->consume(function ($envelope,$queue){ $msg = $envelope->getBody(); echo $msg."\n"; //处理消息 $queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答 }); //$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答 } $this->close(); } } try{ (new ConsumerMQ)->run(); }catch (\Exception $exception){ var_dump($exception->getMessage()) ; }转载:脚本之家https://www.jb51.net/article/158162.htm
2022年06月23日
291 阅读
0 评论
0 点赞
2022-06-23
【JS】解决百度统计被刷广告的办法,屏蔽非法广告
百度统计被刷广告的原理想要斩草除根,就需要知己知彼,明白他们操作的原理以后,就可以有效的遏制他们无下限的刷广告。他们是这样做的,先写一个机器爬虫,爬取页面内容,寻找「hm.baidu.com/hm.js」如果找到了就说明你使用了百度统计,然后再取后面的参数,就得到了你的百度统计ID,然后保存到数据库,拿到你的的百度统计ID以后,就可以进行下一步,使用程序批量的给百度统计接口发送数据,也就是说这些内容他们根本不是真的去你的网站,而是直接给百度统计的接口提交了数据,对你的网站流量没有任何贡献,你只有看广告的份。我也是被刷广告刷的很严重,是在忍无可忍,必须解决一下他们了。百度统计给我们的代码:<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?868e22cfca30f68b16f4c32562c7e649"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>将百度统计的特征,也就是地址打散成数组,再组装使用:var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); var analytics_bd = '868e22cfca30f68b16f4c32562c7e649'; hm.src = ['ht', 't', 'ps', ':/', '/h', 'm', '.', 'ba', 'i', 'd', 'u.c', 'o', 'm/', 'h', 'm', '.j', 's?', analytics_bd].join(''); var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);})();需要改动的就只有一行,注意「hm.src = "https://hm.baidu.com/hm.js?868e22cfca30f68b16f4c32562c7e649";」这行代码,被拆散为数组,然后再拼装起来使用。这样他们的爬虫程序就无法识别到你在使用百度统计了。
2022年06月23日
293 阅读
0 评论
0 点赞
2022-06-23
【PHP】 使用PHPoffice实现普通的导出功能(二)
第二版 使用PHPoffice实现普通的导出功能<?php namespace app\index\controller; use app\index\controller\Comm; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use think\Exception; use think\Request; class Importsheet extends comm{ private $sheet_filename; private $sheet_name; private $sheet_firstline = []; private $sheet_info = []; private $imgSwitch; private $switch; private $path = "uploads/excel/"; /** * Importsheet constructor. * @param $filename 文件名 * @param $name sheet名 * @param $firstline 表头 * @param $info 表内容 * @param $imgSwitch 图片开关 * @param $switch 返回下载地址还是直接返回文件 */ public function __construct($filename='Default',$name='sheet1',$firstline = [],$info = [],$imgSwitch = false, $switch = false) { parent::__construct(); $this->sheet_filename = $filename; $this->sheet_name = $name; $this->sheet_firstline = $firstline; $this->sheet_info = $info; $this->imgSwitch = $imgSwitch; $this->switch = $switch; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2020/12/23 16:08 * @param int $column_num * @return mixed * @Description:获取表格列数的字母 */ public function getMaxColumn(int $column_num) { try{ if(empty($column_num)){ throw new Exception('column_num:列数为空~'); } if(!is_int($column_num)){ throw new Exception('column_num:参数类型错误~'); } if($column_num > 26*26 || $column_num < 0){ throw new Exception('最大列数:676列,最小列数:1列'); } $column_word = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; //生成循环次数 $num = ceil($column_num/26); for($c = 0; $c < $num; $c++) { $first_word = $column_word[$c-1]; foreach($column_word as $key => $val){ if($c >= 1){ $word = $first_word.$column_word[$key]; }else{ $word = $column_word[$key]; } $column[] = $word; } } for($a = 0; $a < $column_num; $a++){ $new_column[] = $column[$a]; } return $new_column; }catch (Exception $e){ returnResponse(100,$e->getMessage()); } } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2020/12/23 17:54 * @Description:输出表 */ public function outputSheet() { try{ $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); //设置sheet的名字 $sheet->setTitle($this->sheet_name); //默认表头第一行 $k = 1; //生成列的个数,根据表头个数来定 $column_num = count($this->sheet_firstline); $info_field_num = count($this->sheet_info[0]); if($column_num != $info_field_num){ if($column_num > $info_field_num){ $better_column_info = '表头多'; }else{ $better_column_info = '数据列多'; } throw new Exception('结果集列数和表头列数不一致~'.$better_column_info); } //生成表头上方的字母(最大676,最小1) $column_word = $this->getMaxColumn($column_num); //设置表头 for($i=0;$i<$column_num;$i++){ $sheet->setCellValue($column_word[$i].$k, $this->sheet_firstline[$i]); } //第二行开始插入数据 $k = 2; //插入表格数据 foreach ($this->sheet_info as $key => $value) { $b = 0; for($a = 0; $a < $column_num; $a++){ $getvalbykey = array_values($value); if($this->imgSwitch){ /*写入图片*/ $files_arr = explode('.', $getvalbykey[$b]); if(!empty($files_arr)){ $file_suffix = array_pop($files_arr); strtolower($file_suffix); $suffix = ['jpg', 'jpeg', 'gif', 'bmp', 'png','pdf','doc','docx','xlsx','xls']; if(in_array($file_suffix,$suffix)){ $str_thumb = str_replace($this->request->domain(),'',$getvalbykey[$b]); $thumb = '/home/wwwroot/xphbk/public'.$str_thumb; if($thumb){ $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $drawing ->setName('图片'); $drawing ->setDescription('图片'); $drawing ->setPath($thumb); $drawing ->setWidth(80); $drawing ->setHeight(80); $drawing ->setCoordinates($column_word[$a].$k); $drawing ->setOffsetX(0); $drawing ->setOffsetY(0); $drawing ->setWorksheet($spreadsheet->getActiveSheet()); } }else{ $sheet->setCellValue($column_word[$a].$k, $getvalbykey[$b]); } $b++; } }else{ $sheet->setCellValue($column_word[$a].$k, $getvalbykey[$b]); $b++; } } $k++; } //文件名 $file_name = date('Y-m-d H:i:s', time()).'-'.rand(1000, 9999).'_'. $this->sheet_filename . ".xlsx"; //下载 header('Content-Type: application/vnd.ms-excel'); header('Content-Disposition: attachment;filename="'.$file_name.'"'); header('Cache-Control: max-age=0'); $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'); if($this->switch == false){ $path = self::createPath($this->path); $writer->save($path.$file_name); // $url = 'http://'.$_SERVER['SERVER_NAME'].'/public/'.$path.$filename; $url = 'http://'.$_SERVER['SERVER_NAME'].'/'.$path.$file_name; }else{ return $writer->save('php://output'); } return $url; }catch (Exception $e){ returnResponse(100,$e->getMessage()); } } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/4/12 13:36 * @param $path * @Description:设置路径判断是否存在,不存在创建 */ private function createPath($path){ $Month = date('Ym',time()); $Day = date('d',time()); $path = $this->path.$Month.'/'.$Day.'/'; if(!is_dir($path)){ header("Content-type:text/html;charset=utf-8"); $res = mkdir(iconv("UTF-8", "GBK", $path),0777,true); if(!$res){ throw new Exception("创建目录失败"); } } return $path; } }使用图片导出的时候注意限制条数,图片导出比较费时间,在我自己的项目中,每次导出大概有五百条数据,是没有问题,具体的还要各位使用者再测使用方法:复制粘贴+稍微改改,就能用了
2022年06月23日
288 阅读
0 评论
0 点赞
2022-06-23
【PHP】使用PHPoffice实现普通的导入功能
解决普通导入的问题,暂不能解决导入图片相关问题,后续遇到相关业务在处理<?php namespace app\index\lib\xlsx; use app\index\lib\xlsx\import as aaa; use think\Controller; use think\Exception; use think\exception\ErrorException; use think\Log; class import extends Controller { /** * 注意事项: * 1. $fileSize已经转换成字节,无需再次转换,所以单位(M),默认5M; * 2. 如果需要更 文件路径,扩展名,记录表名,在公共定义的地方修改即可; * 3. 导入记录默认开启,如果关闭,将不再有数据表的记录任何信息(不包含相关日志); * 4. 关闭导入记录 将true改为false; * 5. 使用前需要引入phpoffice扩展; * 6. 如果需要重新处理表格数据, * 6-1. 将$dealData 传值 true; * 6-2. 需要重新调用批量添加的方法$this->insertAllData($data); * * 使用方法: * 1. 实例化当前类 * 2. 同时传参(*:必须;-:非必须): *表名 *表字段 *上传文件接收的名字 -是否需要返回表格数据 -文件限制大小 * 3. 调用upload方法即可 * * * 示例: * namespace app\index\controller; * use think\Controller; * use app\index\lib\xlsx\import as aaa; * class A extends Controller * { * public function aa(){ * $tableFile = ['a','b','c','d','e','f','g','h']; * $imports = new aaa('test',$tableFile,'file'); * if($this->request->file('file')){ * return $imports->upload(); * }else{ * return "<form action='/public/index.php/index/a/aa' enctype='multipart/form-data' method='post'> * <input type='file' name='file' /> <br> * <input type='submit' value='上传' /> * </form> "; * } * } * } */ public $fileSize; //文件大小 注意:已经转换成字节,所以单位(M) public $fileName; //接受文件的名字 public $filePath = 'uploads/importxlsx'; //文件路径 public $suffix; //文件后缀 public $suffixArr = ['xlsx', 'xls', 'Xlsx', 'Xls']; //文件后缀范围 public $tableName; //数据表名 public $tableField; //数据表字段 public $switchLog = true; //导入记录 public $logTableName = 'xlsximportlog'; //记录表名称 public $dealData; //是否返回数据重新处理 /** * import constructor. 实例化自动执行 * @param string $tableName 表名 * @param array $tableField 数据库字段 * @param string $fileName 上传文件的名字 * @param string $fileSize 长传文件的大小 注意:已经转换成字节,所以单位(M) */ public function __construct($tableName = '', $tableField = [], $fileName = '', $dealData = false, $fileSize = '5') { parent::__construct(); $this->tableName = $tableName; $this->tableField = $tableField; $this->fileName = $fileName; $this->dealData = $dealData; $this->fileSize = $fileSize*1024*1024; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/2 15:07 * @Description:单文件上传 * 成功上传后 获取上传信息 *print_r($file->getInfo()); *[ * ['name'] => '2021-07-31 15_50_18-5822_商务部在办企业进度及时限表.xlsx', * ['type'] => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', * ['tmp_name'] => 'C:\Users\EDZ\AppData\Local\Temp\phpEAA4.tmp', * ['error'] => '0', * ['size'] => '132892' *]; *echo $info->getExtension(); //输出 jpg *echo $info->getSaveName(); //输出 20160820/42a79759f284b767dfcb2a0197904287.jpg *echo $info->getFilename(); //输出 42a79759f284b767dfcb2a0197904287.jpg */ public function upload(){ $file = request()->file($this->fileName); $path = ROOT_PATH . 'public' . DS . $this->filePath; $info = $file->validate(['size'=>$this->fileSize,'ext' => $this->suffixArr])->move($path); if($info){ $this->path = $path.'/'.$info->getSaveName(); $this->suffix = $info->getExtension(); $res = self::import($file->getInfo()); if($this->switchLog){ self::importLog($this->path,count($res),$file->getInfo['name'],$file->getInfo['size'],$file->getInfo['type']); } if($this->dealData){ return $res; } $result = $this->insertAllData($res); if($result){ return $result; } throw new Exception('添加失败'); }else{ // 上传失败获取错误信息 throw new Exception($file->getError()); } } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/3 18:05 * @throws \PhpOffice\PhpSpreadsheet\Exception * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception * @Description:导入逻辑 */ private function import($fileInfo = []){ $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader(ucfirst($this->suffix)); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load($this->path); //载入excel表格 $worksheet = $spreadsheet->getActiveSheet(); $highestRow = $worksheet->getHighestRow(); // 总行数 $highestColumn = $worksheet->getHighestColumn(); // 总列数 $highestColumnNum = self::getColumnArrByMaxCol($highestColumn); //列值 if(count($highestColumnNum) != count($this->tableField)) throw new Exception("表格的列数和表字段个数不相同,请确认后再试~"); $lines = $highestRow - 1;//减掉表的第一行(通常第一行是表头,不需要导入,导入时无需删除第一行) if ($lines <= 0) throw new Exception($fileInfo['name']."Excel表格中没有数据~"); $b = 0; for ($i = 2; $i <= $highestRow; $i++){ for($a = 0; $a <= count($highestColumnNum); $a++){ $data[$b][$this->tableField[$a]] = $worksheet->getCellByColumnAndRow($a+1,$i)->getValue(); } array_pop($data[$b]); $b++; } return $data; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/4 14:35 * @param string $col * @throws Exception * @Description:根据列数的最大值返回最大范围内的列值 */ public function getColumnArrByMaxCol($col = ''){ if(empty($col)) throw new Exception("不能为空~"); if(strlen($col) > 2) throw new Exception("导入列数超出最大值~"); strtoupper($col); $column_word = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; $colArr = []; if(in_array($col,$column_word)){ for ($i=0;$i<=25;$i++){ if($column_word[$i] == $col) { array_push($colArr,$column_word[$i]); return $colArr; } array_push($colArr,$column_word[$i]); } } $firstColumn = substr($col,0,1); $twoColumn = substr($col,1,1); if(!in_array($firstColumn,$column_word)) throw new Exception("错误的参数"); if(!in_array($twoColumn,$column_word)) throw new Exception("错误的参数"); $firstColumnNum = array_keys($column_word,$firstColumn); // $twoColumnNum = array_keys($column_word,$twoColumn); $colArr = $column_word; for($a = 0; $a <= $firstColumnNum[0]; $a++){ for($b = 0; $b <= 25; $b++){ if($twoColumn == $column_word[$b] && $firstColumnNum[0] == $a){ array_push($colArr,$column_word[$a].$column_word[$b]); return $colArr; } array_push($colArr,$column_word[$a].$column_word[$b]); } } } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/3 17:26 * @param $data * @return $this * @Description:添加全部数据 */ public function insertAllData($data = ''){ if(empty($data) || !is_array($data)) throw new Exception("导入的数据是空的"); $res = Db($this->tableName)->insertAll($data); if($res){ return $this; } throw new Exception("添加失败"); } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/3 16:53 * @param string $url * @param string $totalRow * @param string $totalInsert * @return string * @Description:日志记录 */ public function importLog($url = '', $totalRow = '', $name = '', $size = '', $type = ''){ if(!$this->switchLog){ return 'Method ERROR:The switchLog function must be enabled'; } if(!self::createTable()) throw new Exception("记录日志失败"); $data = [ 'create_time' => time(), 'url' => $url, 'name' => $name, 'size' => $size, 'type' => $type, 'num' => $totalRow, ]; $res = Db($this->logTableName)->insert($data); if($res){ return $this; } return "Error Message:Log recording failure"; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/3 11:59 * @param $xlsxTableName * @return bool|string * @throws \think\db\exception\BindParamException * @throws \think\exception\PDOException * @Description:创建数据表 */ private function createTable(){ $xlsxTableName = config('database.prefix').$this->logTableName; $isTable=db()->query('SHOW TABLES LIKE '."'". $xlsxTableName ."'"); if($isTable){ return $this; } $database = config('database.database'); $sql = "CREATE TABLE `". $database ."`.`". $xlsxTableName ."` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增', `url` varchar(255) NULL COMMENT 'url地址', `name` varchar(255) NULL COMMENT '源文件名名称', `size` varchar(40) NULL COMMENT '源文件大小', `type` varchar(255) NULL COMMENT '源文件类型', `num` int(11) NULL COMMENT '总数量', `create_time` bigint(20) NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) COMMENT = '导入xlsx表格记录表'"; $res = db()->query($sql); if($res){ return $this; } return "Query Message:Error creating data table"; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/8/3 17:18 * @param string $page * @param string $limit * @param string $sort * @return bool|false|\PDOStatement|string|\think\Collection * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException * @Description:获取日志列表 */ public function getLogList($page = '1', $limit = '20', $sort = 'desc'){ $res = Db($this->logTableName)->page($page,$limit)->order('id',$sort)->select(); return $res; } }
2022年06月23日
817 阅读
0 评论
0 点赞
2022-06-23
【API】获取根据QQ号获取头像
1.API接口:http://q1.qlogo.cn/g?b=qq&nk=QQ号码&s=6402.调用例子:http://q1.qlogo.cn/g?b=qq&nk=3584685883&s=640
2022年06月23日
325 阅读
0 评论
0 点赞
2022-06-23
【PHP】获取每个月的开始和结束的时间戳
/** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/6/9 10:21 * @param $year * @Description:获取任意一年的每个月的开始和结束的时间戳 */ public function getMonthBetweenTimestampByYear($year = ''){ if(empty($year)){ $year = date('Y',time()); } $month = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']; foreach($month as $key => $val){ $month_start = strtotime($year.'-'.$val);//指定月份月初时间戳 $month_end = mktime(23, 59, 59, $val+1, 00,$year);//指定月份月末时间戳 $month_s_e[] = ['start' => $month_start, 'end' => $month_end]; } return $month_s_e; }
2022年06月23日
268 阅读
0 评论
0 点赞
2022-06-23
【PHP】威尔逊得分(Wilson Score)排序算法
威尔逊得分(Wilson Score)排序算法:是对质量进行排序,评论中含有好评还有差评,综合考虑评论数与好评率,得分越高,质量越高。u表示正例数(好评),v表示负例数(差评),n表示实例总数(评论总数),p表示好评率,z是正态分布的分位数(参数),S表示最终的威尔逊得分。z一般取值2即可,即95%的置信度。正太分布的分位数表:算法性质:性质:得分S的范围是[0,1),效果:已经归一化,适合排序性质:当正例数u为0时,p为0,得分S为0;效果:没有好评,分数最低;性质:当负例数v为0时,p为1,退化为1/(1 + z^2 / n),得分S永远小于1;效果:分数具有永久可比性;性质:当p不变时,n越大,分子减少速度小于分母减少速度,得分S越多,反之亦然;效果:好评率p相同,实例总数n越多,得分S越多;性质:当n趋于无穷大时,退化为p,得分S由p决定;效果:当评论总数n越多时,好评率p带给得分S的提升越明显;性质:当分位数z越大时,总数n越重要,好评率p越不重要,反之亦然;效果:z越大,评论总数n越重要,区分度低;z越小,好评率p越重要;PHP实现: <?php echo wilsonScore_2(300, 13/300, 2); function wilsonScore_2($n, $p, $z){ $a_1 = ((1 / (2 * $n)) * ( pow($z,2))); $b_1 = $z * sqrt(($p * (1 - $p) / $n) + ( pow($z,2) / (4 * (pow($n,2))))); $c_1 = 1 + ((1 / $n) * ( pow($z,2))); $numerator = $p + $a_1 - $b_1; $res = $numerator/$c_1; return $res; }参考引用:https://www.fujieace.com/php/algorithm/score.html
2022年06月23日
393 阅读
0 评论
0 点赞
2022-06-23
【PHP】希尔排序
思路分析:希尔排序是基于插入排序的,区别在于插入排序是相邻的一个个比较(类似于希尔中h=1的情形),而希尔排序是距离h的比较和替换。希尔排序中一个常数因子n,原数组被分成各个小组,每个小组由h个元素组成,很可能会有多余的元素。当然每次循环的时候,h也是递减的(h=h/n)。第一次循环就是从下标为h开始。希尔排序的一个思想就是,分成小组去排序 function shellsort(array $arr){ // 将$arr按升序排列 $len = count($arr); $f = 3;// 定义因子 $h = 1;// 最小为1 while ($h < $len/$f){ $h = $f*$h + 1; // 1, 4, 13, 40, 121, 364, 1093, ... } while ($h >= 1){ // 将数组变为h有序 for ($i = $h; $i < $len; $i++){ // 将a[i]插入到a[i-h], a[i-2*h], a[i-3*h]... 之中 (算法的关键) for ($j = $i; $j >= $h; $j -= $h){ if ($arr[$j] < $arr[$j-$h]){ $temp = $arr[$j]; $arr[$j] = $arr[$j-$h]; $arr[$j-$h] = $temp; } //print_r($arr);echo '<br/>'; // 打开这行注释,可以看到每一步被替换的情形 } } $h = intval($h/$f); } return $arr; }
2022年06月23日
269 阅读
0 评论
0 点赞
2022-06-23
【PHP】冒泡排序
在要排序的一组数中,对当前还未排好的序列,从前往后对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即,每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换function bubbleSort ($arr) { $len = count($arr); //该层循环控制 需要冒泡的轮数 for ($i=1; $i<$len; $i++) { //该层循环用来控制每轮 冒出一个数 需要比较的次数 for ($k=0; $k<$len-$i; $k++) { if($arr[$k] > $arr[$k+1]) { $tmp = $arr[$k+1]; // 声明一个临时变量 $arr[$k+1] = $arr[$k]; $arr[$k] = $tmp; } } } return $arr; }
2022年06月23日
316 阅读
0 评论
0 点赞
2022-06-23
【PHP】选择排序
在要排序的一组数中,选出最小的一个数与第一个位置的数交换。然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。function selectsort($arr) { //$i 当前最小值的位置, 需要参与比较的元素 for($i=0, $len=count($arr); $i<$len-1; $i++) { //先假设最小的值的位置 $p = $i; //$j 当前都需要和哪些元素比较,$i 后边的。 for($j=$i+1; $j<$len; $j++) { //$arr[$p] 是 当前已知的最小值 if($arr[$p] > $arr[$j]) { //比较,发现更小的,记录下最小值的位置;并且在下次比较时,应该采用已知的最小值进行比较。 $p = $j; } } //已经确定了当前的最小值的位置,保存到$p中。 //如果发现 最小值的位置与当前假设的位置$i不同,则位置互换即可 if($p != $i) { $tmp = $arr[$p]; $arr[$p] = $arr[$i]; $arr[$i] = $tmp; } } //返回最终结果 return $arr; }
2022年06月23日
311 阅读
0 评论
0 点赞
2022-06-23
【PHP】插入排序
在要排序的一组数中,假设前面的数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。function insertsort($arr) { $len=count($arr); for($i=1; $i<$len; $i++) { //获得当前需要比较的元素值。 $tmp = $arr[$i]; //内层循环控制 比较 并 插入 for($j=$i-1; $j>=0; $j--) { //$arr[$i];//需要插入的元素; $arr[$j];//需要比较的元素 if($tmp < $arr[$j]) { //发现插入的元素要小,交换位置 //将后边的元素与前面的元素互换 $arr[$j+1] = $arr[$j]; //将前面的数设置为 当前需要交换的数 $arr[$j] = $tmp; } else { //如果碰到不需要移动的元素 //由于是已经排序好是数组,则前面的就不需要再次比较了。 break; } } } //将这个元素 插入到已经排序好的序列内。 //返回 return $arr; }
2022年06月23日
282 阅读
0 评论
0 点赞
2022-06-23
【PHP】快速排序
快速排序选择一个基准元素,通常选择第一个元素或者最后一个元素。通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素。此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。 function quicksort($arr) { //判断参数是否是一个数组 if(!is_array($arr)) return false; //递归出口:数组长度为1,直接返回数组 $length = count($arr); if($length<=1) return $arr; //数组元素有多个,则定义两个空数组 $left = $right = array(); //使用for循环进行遍历,把第一个元素当做比较的对象 for($i=1; $i<$length; $i++) { //判断当前元素的大小 if($arr[$i]<$arr[0]){ $left[]=$arr[$i]; }else{ $right[]=$arr[$i]; } } //递归调用 $left=quicksort($left); $right=quicksort($right); //将所有的结果合并 return array_merge($left,array($arr[0]),$right); }
2022年06月23日
307 阅读
0 评论
0 点赞
2022-06-23
【RabbitMQ】简介以及使用场景
一. RabbitMQ 简介MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。二. RabbitMQ 使用场景解耦(为面向服务的架构(SOA)提供基本的最终一致性实现)场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败订单系统与库存系统耦合引入消息队列订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦为了保证库存肯定有,可以将队列大小设置成库存数量,或者采用其他方式解决。基于消息的模型,关心的是“通知”,而非“处理”。短信、邮件通知、缓存刷新等操作使用消息队列进行通知。消息队列和RPC的区别与比较:RPC: 异步调用,及时获得调用结果,具有强一致性结果,关心业务调用处理结果。消息队列:两次异步RPC调用,将调用内容在队列中进行转储,并选择合适的时机进行投递(错峰流控)2. 异步提升效率场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间(3)引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:3. 流量削峰流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛应用场景:系统其他时间A系统每秒请求量就100个,系统可以稳定运行。系统每天晚间八点有秒杀活动,每秒并发请求量增至1万条,但是系统最大的处理能力只能每秒处理1000个请求,于是系统崩溃,服务器宕机。之前架构:大量用户(100万用户)通过浏览器在晚上八点高峰期同时参与秒杀活动。大量的请求涌入我们的系统中,高峰期达到每秒钟5000个请求,大量的请求打到MySQL上,每秒钟预计执行3000条SQL。但是一般的MySQL每秒钟扛住2000个请求就不错了,如果达到3000个请求的话可能MySQL直接就瘫痪了,从而系统无法被使用。但是高峰期过了之后,就成了低峰期,可能也就1万用户访问系统,每秒的请求数量也就50个左右,整个系统几乎没有任何压力。引入MQ:100万用户在高峰期的时候,每秒请求有5000个请求左右,将这5000请求写入MQ里面,系统A每秒最多只能处理2000请求,因为MySQL每秒只能处理2000个请求。系统A从MQ中慢慢拉取请求,每秒就拉取2000个请求,不要超过自己每秒能处理的请求数量即可。MQ,每秒5000个请求进来,结果只有2000个请求出去,所以在秒杀期间(将近一小时)可能会有几十万或者几百万的请求积压在MQ中。这个短暂的高峰期积压是没问题的,因为高峰期过了之后,每秒就只有50个请求进入MQ了,但是系统还是按照每秒2000个请求的速度在处理,所以说,只要高峰期一过,系统就会快速将积压的消息消费掉。我们在此计算一下,每秒在MQ积压3000条消息,1分钟会积压18万,1小时积压1000万条消息,高峰期过后,1个多小时就可以将积压的1000万消息消费掉。三. 引入消息队列的优缺点优点优点就是以上的那些场景应用,就是在特殊场景下有其对应的好处,解耦、异步、削峰。缺点系统的可用性降低系统引入的外部依赖越多,系统越容易挂掉,本来只是A系统调用BCD三个系统接口就好,ABCD四个系统不报错整个系统会正常运行。引入了MQ之后,虽然ABCD系统没出错,但MQ挂了以后,整个系统也会崩溃。系统的复杂性提高引入了MQ之后,需要考虑的问题也变得多了,如何保证消息没有重复消费?如何保证消息不丢失?怎么保证消息传递的顺序?一致性问题A系统发送完消息直接返回成功,但是BCD系统之中若有系统写库失败,则会产生数据不一致的问题。总结所以总结来说,消息队列是一种十分复杂的架构,引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避。引入MQ系统复杂度提升了一个数量级,但是在有些场景下,就是复杂十倍百倍,还是需要使用MQ。
2022年06月23日
293 阅读
0 评论
0 点赞
2022-06-23
【JS】静止事件的脚本
//关闭右键 document.oncontex`请输入代码`tmenu=function(){event.returnValue=false;} //关闭所有f2-f12快捷键 屏蔽 Ctrl+n document.onkeydown=function(){if(111<event.keyCode<123||(event.ctrlKey&&(event.keyCode==78))){event.keyCode=0;event.returnValue=false;}} //关闭 f1帮助 document.onhelp=function(){event.returnValue=false;} //关闭shift+点击链接 document.onclick=function(){if(event.shiftKey&&(event.srcElement.tagName=="A")){event.returnValue=false;}} //禁止拷贝 document.oncopy=function(){event.returnValue=false;} //禁止选择 document.onselectstart=function(){event.returnValue=false;} //禁止粘贴 document.onpaste=function(){event.returnValue=false;} //禁止 退格键的 前进后退功能 document.onkeydown = function() {}//{if (event.keyCode==8) {event.returnValue=false;}} //禁止打印 window.onbeforeprint=function(){window.document.body.style.display="none"}
2022年06月23日
261 阅读
0 评论
0 点赞
2022-06-23
【PHP】魔术方法
__constuct: 构建对象的时被调用;__destruct: 明确销毁对象或脚本结束时被调用;__set: 当给不可访问或不存在属性赋值时被调用__get: 读取不可访问或不存在属性时被调用__isset: 对不可访问或不存在的属性调用isset()或empty()时被调用__unset: 对不可访问或不存在的属性进行unset时被调用__call: 调用不可访问或不存在的方法时被调用__callStatic: 调用不可访问或不存在的静态方法时被调用__sleep: 当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用__wakeup: 当使用unserialize时被调用,可用于做些对象的初始化操作__clone: 进行对象clone时被调用,用来调整对象的克隆行为__toString: 当一个类被转换成字符串时被调用__invoke: 当以函数方式调用对象时被调用__set_state: 当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。__debuginfo: 当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本 <?php class Magic { public $var = 'test'; //构造函数,在创建对象的时候调用 public function __construct() { echo '__construct called'.PHP_EOL; } //某个对象的引用都被删除、对象被销毁、调用exit()后、脚本关闭时被调用 public function __destruct() { echo '__destruct called'.PHP_EOL; } //当给不可访问或不存在属性赋值时被调用 public function __set($name, $value) { echo $name.'-'.$value; echo '__set called'.PHP_EOL; } //读取不可访问或不存在属性时被调用 public function __get($name) { echo $name; echo '__get called'.PHP_EOL; } //调用不可访问或不存在的方法时被调用 public function __call($name, $arguments) { echo $name . '-' . implode(',', $arguments); echo '__call called'.PHP_EOL; } //调用不可访问或不存在的静态方法时被调用 public static function __callStatic($name, $arguments) { echo $name . '-' . implode(',', $arguments); echo '__callStatic called'.PHP_EOL; } //对不可访问或不存在的属性调用isset()或empty()时被调用 public function __isset($name) { echo $name; echo '__isset called'.PHP_EOL; return true; } //对不可访问或不存在的属性进行unset时被调用 public function __unset($name) { echo $name; echo '__unset called'.PHP_EOL; } //serialize时被调用,当你不需要保存大对象的所有数据时很有用 public function __sleep() { echo '__sleep called'.PHP_EOL; return array('var1111111111'); } //unserialize时被调用,可用于做些对象的初始化操作 public function __wakeup() { echo '__wakeup called'.PHP_EOL; $this->var = 'test after wakeup'; } //当一个类被转换成字符串时被调用 public function __toString() { return '__toString called'.PHP_EOL; } //进行对象clone时被调用,用来调整对象的克隆行为 public function __clone() { echo '__clone called'.PHP_EOL; } //当以函数方式调用对象时被调用 public function __invoke() { echo '__invoke called'.PHP_EOL; } //当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。 public static function __set_state($arr) { return '__set_state called'.PHP_EOL; } //当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本 public function __debuginfo($arr) { echo '__debuginfo called'.PHP_EOL; return array( 'var' => 'test fro __debuginfo' ); } } $m = new Magic(); //__construct()被调用 $m->not_exist_property = test; //__set()被调用 echo $m->not_exist_property;//__get()被调用 $m->abc(1,2,3); //__call()被调用 echo isset($m->not_exist_property); //__isset()被调用,返回bool值 unset($m->not_exist_property); //__unset()被调用 echo $tmp = serialize($m); //__sleep()被调用 unserialize($tmp); //__wakeup()被调用 $m1 = clone $m; //__clone()被调用,对象默认是引用传递,使用clone关键词则可实现对象复制 $m(); //__invoke() eval( '$m2 = ' . var_export ( $m , true ) . ';' );var_dump($m2); var_dump($m); //最后__destruct()被调用 /* 结果: __construct called not_exist_property-test__set called not_exist_property__get called abc-1,2,3__call called not_exist_property__isset called 1not_exist_property__unset called __sleep called O:5:"Magic":1:{s:13:"var1111111111";N;}__wakeup called __destruct called __clone called __invoke called string(20) "__set_state called " class Magic#1 (1) { public $var => string(4) "test" } __destruct called __destruct called */
2022年06月23日
296 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP实现链式操作
实现链式操作的核心方法就是 : return $this;下面是例子: /** * 设置是否严格检查字段名 * @access public * @param bool $strict 是否严格检查字段 * @return $this */ public function strict(bool $strict = true) { $this->options['strict'] = $strict; return $this; } /** * 设置自增序列名 * @access public * @param string $sequence 自增序列名 * @return $this */ public function sequence(string $sequence = null) { $this->options['sequence'] = $sequence; return $this; } /** * 设置JSON字段信息 * @access public * @param array $json JSON字段 * @param bool $assoc 是否取出数组 * @return $this */ public function json(array $json = [], bool $assoc = false) { $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; return $this; }
2022年06月23日
243 阅读
0 评论
0 点赞
2022-06-23
【PHP】SPL四种常用的数据结构
spl,指SPL - Standard PHP Library 标准PHP类库 1.栈【先进后出】 <span style="font-size:18px;">$stack = new SplStack(); $stack->push('data1'); $stack->push('data2'); $stack->push('data3'); echo $stack->pop(); //输出结果为 //data3</span><span style="font-size:24px;font-weight: bold;"> </span> 2.队列【先进先出 后进后出】 <span style="font-size:18px;">$queue = new SplQueue(); $queue->enqueue("data1"); $queue->enqueue("data2"); $queue->enqueue("data3"); echo $queue->dequeue(); //输出结果为 //data1</span> 3.堆 <span style="font-size:18px;">$heap = new SplMinHeap(); $heap->insert("data1"); $heap->insert("data2"); echo $heap->extract(); //输出结果为 //data1</span> 4.固定尺寸数组 <span style="font-size:18px;">$array = new SplFixedArray(5); $array[0]=1; $array[3]=3; $array[2]=2; var_dump($array); //输出结果为 // object(SplFixedArray)[1] // public 0 => int 1 // public 1 => null // public 2 => int 2 // public 3 => int 3 // public 4 => null</span>
2022年06月23日
330 阅读
0 评论
0 点赞
2022-06-23
【PHP】PHP实现JWT生成和验证
JWT里面的 encode 和 decode方法进行生成token和验证token /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/6/17 16:14 * @param $uid * @return mixed * @Description:生成token */ public function setToken($uid){ //私钥 $privateKey = <<<EOD -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9 5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1 cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5 5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM= -----END RSA PRIVATE KEY----- EOD; $payload = [ 'iss'=>'xiaopohai', //签发人(官方字段:非必需) 'exp'=>time()+3600*24*7, //过期时间(官方字段:非必需) 'aud'=>'everyone', //受众(官方字段:非必需) 'nbf'=>time(), //生效时间(官方字段:非必需) 'iat'=>time(), //签发时间(官方字段:非必需) 'userInfo'=>[ 'id' =>$uid, ], ]; $token = JWT::encode($payload,$privateKey,'HS256'); return $token; } /** * @Author: 小破孩嫩 * @Email: 3584685883@qq.com * @Time: 2021/6/17 16:01 * @param $token * @return int[] * @Description:验证token */ public function checkToken($token){ try{ //公钥 $publicKey = <<<EOD -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H 4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t 0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4 ehde/zUxo6UvS7UrBQIDAQAB -----END PUBLIC KEY----- EOD; $res = JWT::decode($token,$publicKey,['HS256']); returnResponse('200','请求成功', $res); }catch (Exception $e) { returnResponse(100,$e->getMessage()); } }学习地址:https://github.com/firebase/php-jwt
2022年06月23日
1,370 阅读
0 评论
0 点赞
2022-06-23
【PHP】RSA 类 和 使用
最新优化的版本,不受长度限制 <?php namespace app\common\lib\pay\tppay; class Rsa { private $privateKey; private $publicKey; public function __construct(?string $privateKeyPath = null, ?string $publicKeyPath = null, ?string $privateKeyContent = null, ?string $publicKeyContent = null) { if ($privateKeyContent !== null) { $this->privateKey = openssl_pkey_get_private($privateKeyContent); } else { $this->privateKey = openssl_pkey_get_private(file_get_contents($privateKeyPath ?? __DIR__ . '/rsa_private_key.pem')); } if ($publicKeyContent !== null) { $this->publicKey = openssl_pkey_get_public($publicKeyContent); } else { $this->publicKey = openssl_pkey_get_public(file_get_contents($publicKeyPath ?? __DIR__ . '/rsa_public_key.pem')); } } /** * 生成数据的SHA1withRSA签名 * @param string $data 需要签名的数据 * @return string 返回base64编码的签名 */ public function sign(string $data): string { if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA1)) { throw new \RuntimeException('签名失败'); } return base64_encode($signature); } /** * 验证数据的SHA1withRSA签名 * @param string $data 原始数据 * @param string $signature 要验证的base64编码的签名 * @return bool 返回签名是否有效 */ public function verify(string $data, string $signature): bool { $signature = base64_decode($signature); $result = openssl_verify($data, $signature, $this->publicKey, OPENSSL_ALGO_SHA1); if ($result === -1) { throw new \RuntimeException('验证签名失败: ' . openssl_error_string()); } return $result === 1; } private function getBlockSize($type = 'private') { $keyDetail = openssl_pkey_get_details($type === 'private' ? $this->privateKey : $this->publicKey); $modulusSize = strlen($keyDetail['rsa']['n']) * 8; // 获取模数的位数 return floor(($modulusSize / 8) - 11); // RSA PKCS#1 v1.5 padding } public function privEncrypt(string $data) { $blockSize = $this->getBlockSize('private'); $encryptedData = ''; while ($data) { $input = substr($data, 0, $blockSize); $data = substr($data, $blockSize); if (!openssl_private_encrypt($input, $encryptedPart, $this->privateKey)) { throw new \RuntimeException('私钥加密失败'); } $encryptedData .= $encryptedPart; } return base64_encode($encryptedData); } public function publicEncrypt(string $data) { $blockSize = $this->getBlockSize('public'); $encryptedData = ''; while ($data) { $input = substr($data, 0, $blockSize); $data = substr($data, $blockSize); if (!openssl_public_encrypt($input, $encryptedPart, $this->publicKey)) { throw new \RuntimeException('公钥加密失败'); } $encryptedData .= $encryptedPart; } return base64_encode($encryptedData); } public function privDecrypt(string $encrypted) { $blockSize = $this->getBlockSize('private'); $decryptedData = ''; $encrypted = base64_decode($encrypted); while ($encrypted) { $input = substr($encrypted, 0, $blockSize + 11); // 加上padding长度 $encrypted = substr($encrypted, $blockSize + 11); if (!openssl_private_decrypt($input, $decryptedPart, $this->privateKey)) { throw new \RuntimeException('私钥解密失败'); } $decryptedData .= $decryptedPart; } return $decryptedData; } public function publicDecrypt(string $encrypted) { $blockSize = $this->getBlockSize('public'); $decryptedData = ''; $encrypted = base64_decode($encrypted); while ($encrypted) { $input = substr($encrypted, 0, $blockSize + 11); $encrypted = substr($encrypted, $blockSize + 11); if (!openssl_public_decrypt($input, $decryptedPart, $this->publicKey)) { throw new \RuntimeException('公钥解密失败'); } $decryptedData .= $decryptedPart; } return $decryptedData; } public function __destruct() { if ($this->privateKey !== null) { openssl_free_key($this->privateKey); } if ($this->publicKey !== null) { openssl_free_key($this->publicKey); } } /** * 创建RSA密钥对 * @param int $bits 密钥位数,默认为2048位 * @param bool $saveToFile 是否将密钥保存到文件,默认为true * @param string|null $privateKeyPath 私钥保存路径,如果$saveToFile为false,则忽略此参数 * @param string|null $publicKeyPath 公钥保存路径,如果$saveToFile为false,则忽略此参数 * @return array 返回包含私钥和公钥的数组 */ public static function createKeyPair($bits = 2048, $saveToFile = true, $privateKeyPath = null, $publicKeyPath = null) { // 创建一个新的私钥和公钥资源 $res = openssl_pkey_new([ "private_key_bits" => $bits, "private_key_type" => OPENSSL_KEYTYPE_RSA, ]); if ($res === false) { throw new \RuntimeException('无法创建RSA密钥对'); } // 提取私钥和公钥 openssl_pkey_export($res, $privateKey); $publicKey = openssl_pkey_get_details($res)['key']; // 如果需要保存到文件 if ($saveToFile) { // 确保目录存在 if ($privateKeyPath !== null && !is_dir(dirname($privateKeyPath))) { mkdir(dirname($privateKeyPath), 0755, true); } if ($publicKeyPath !== null && !is_dir(dirname($publicKeyPath))) { mkdir(dirname($publicKeyPath), 0755, true); } // 保存私钥和公钥到文件 file_put_contents($privateKeyPath ?? __DIR__ . '/rsa_private_key.pem', $privateKey); file_put_contents($publicKeyPath ?? __DIR__ . '/rsa_public_key.pem', $publicKey); } return [ 'privateKey' => $privateKey, 'publicKey' => $publicKey, ]; } } // $rsa = new Rsa('/path/to/private_key.pem', '/path/to/public_key.pem'); // $rsa = new Rsa(null, null, '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----'); //直接获取密钥对(不保存) //$keyPair = Rsa::createKeyPair(2048, false); //echo "Private Key:\n" . $keyPair['privateKey']; //echo "Public Key:\n" . $keyPair['publicKey']; //将密钥对保存到指定位置 //$keyPair = Rsa::createKeyPair(2048, true, '/path/to/rsa_private_key.pem', '/path/to/rsa_public_key.pem'); 下载开源RSA密钥生成工具openssl(通常Linux系统都自带该程序),解压缩至独立的文件夹,进入其中的bin目录,执行以下命令: a、openssl genrsa -out rsa_private_key.pem 1024 b、openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem c、openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem 第一条命令生成原始 RSA私钥文件 rsa_private_key.pem 第二条命令将原始 RSA私钥转换为 pkcs8格式 第三条生成RSA公钥 rsa_public_key.pem 上面几个就可以看出:通过私钥能生成对应的公钥RSA类: <?php class Rsa { /** * 获取私钥 * @return bool|resource */ private static function getPrivateKey() { $abs_path = dirname(__FILE__) . '/rsa_private_key.pem'; $content = file_get_contents($abs_path); return openssl_pkey_get_private($content); } /** * 获取公钥 * @return bool|resource */ private static function getPublicKey() { $abs_path = dirname(__FILE__) . '/rsa_public_key.pem'; $content = file_get_contents($abs_path); return openssl_pkey_get_public($content); } /** * 私钥加密 * @param string $data * @return null|string */ public static function privEncrypt($data = '') { if (!is_string($data)) { return null; } return openssl_private_encrypt($data,$encrypted,self::getPrivateKey()) ? base64_encode($encrypted) : null; } /** * 公钥加密 * @param string $data * @return null|string */ public static function publicEncrypt($data = '') { if (!is_string($data)) { return null; } return openssl_public_encrypt($data,$encrypted,self::getPublicKey()) ? base64_encode($encrypted) : null; } /** * 私钥解密 * @param string $encrypted * @return null */ public static function privDecrypt($encrypted = '') { if (!is_string($encrypted)) { return null; } return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, self::getPrivateKey())) ? $decrypted : null; } /** * 公钥解密 * @param string $encrypted * @return null */ public static function publicDecrypt($encrypted = '') { if (!is_string($encrypted)) { return null; } return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, self::getPublicKey())) ? $decrypted : null; } } **demo:** <?php require_once "Rsa.php"; $rsa = new Rsa(); $data['name'] = 'Tom'; $data['age'] = '20'; $privEncrypt = $rsa->privEncrypt(json_encode($data)); echo '私钥加密后:'.$privEncrypt.'<br>'; $publicDecrypt = $rsa->publicDecrypt($privEncrypt); echo '公钥解密后:'.$publicDecrypt.'<br>'; $publicEncrypt = $rsa->publicEncrypt(json_encode($data)); echo '公钥加密后:'.$publicEncrypt.'<br>'; $privDecrypt = $rsa->privDecrypt($publicEncrypt); echo '私钥解密后:'.$privDecrypt.'<br>'; 也有一些网站提供生成rsa公钥私钥的服务:http://www.bm8.com.cn/webtool/rsa/文章参考:https://www.cnblogs.com/xuweiqiang/p/9784584.html
2022年06月23日
628 阅读
1 评论
0 点赞
2022-06-23
【PHP】单点登录(SingleSignOn,SSO)的思路
用到的工具:JWP类, RSA非对称加密类composer require firebase/php-jwt;JWT包含三部分数据:Header:头部,通常头部有两部分信息:1.token类型:JWT2.加密方式:base64(HS256)Payload:载荷,就是有效数据,一般包含下面信息:1.用户身份信息(注意,这里因为采用base64编码,可解码,因此不要存放敏感信息)2.注册声明:如token的签发时间,过期时间,签发人等这部分也会采用base64编码,得到第二部分数据Signature:签名,是整个数据的认证信息。根据前两步的数据,再加上指定的密钥(secret)(不要泄漏,最好周期性更换),通过base64编码生成。用于验证整个数据完整和可靠性RSA非对称加密:需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。将私钥private_key.pem用在服务器端,公钥发放给android跟ios等前端就是将非对称加密的信息传递到JWT里面的做解密验证
2022年06月23日
244 阅读
0 评论
0 点赞
1
...
6
7
8
...
12