宝塔CC时提示被屏蔽 cc时直接抛出416不是很友好,而且有撞库的问题. function drop -- 如果已经被标记,则在这个地方抛出416 function cc -- 计数器.首次触发和标记 补充html输出提醒 return_html22(config['cc']['status'],'您的请求异常已被屏蔽,有需要请联系管理员(QQ:243008827)! ') svn 调整为主线分支类型,或者将某个目录的提交记录,导入到另一个库的某个分支中. #将reposA导出到一个文件中 svnadmin dump reposA/ > f1 -r xx:xx 指定版本范围 --incremental 只进行增量导出.少这个会导致最后版本的整个目录被导出 #将reposA中的code过滤出来,svndumpfilter include表示只保留制定的目录和文件 svndumpfilter include dirB/code < f1 > f2 #将dumpfile2导入相应的目录,也就是reposB/dirAA/,用parent-dir来指定 svnadmin load reposB --parent-dir dirAA < f2 提交步骤是合并分支到主线,再提交.如果需要查看分支的日志,日志查询时,勾选底部显示合并分支日志. fish && make; and make install phash mysql 用法 BIT_COUNT(img_dhash ^ b'01111') ,如果不是以b'xxx'形式.直接000111形式.会导致前面的数据丢失,出现奇怪的bit_count 感知算法走的是灰度.所以对颜色变化不敏感. 同时, 感知算法.更像是图片颜色分布识别. 并不能很好的识别图像以及内容....没有学习库还是不行. 考虑走百度的图片库或淘宝的商品图片库进行商品图片识别. 百度商品图片识别已测试.符合预期效果 百度入库一天1w免费,搜索500免费.基本够用(不够用就看老板钱包了~). nginx 一键防火墙 https://zhih.me/ngx-lua-waf/ nginx 编译后 在源码的 objs 里,要注意. nginx -V 观察编译参数 安装后,如果原来有安装后.替换 /usr/local/sbin/nginx 以及 /usr/local/nginx/sbin/ 测试参数 /xxx.tar /xxx.sql 等. /?id=1 or 1=1 无效....,需要自行调整配置 elasticsearch https://www.elastic.co/guide/cn/elasticsearch/php/current/_search_operations.html elasticsearch sql https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-xpack.html 配合postman测试 php 扩展 https://github.com/elastic/elasticsearch-x-pack-php 中文分词插件 https://www.jianshu.com/p/bb89ad7a7f7d elasticsearch install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.5.0/elasticsearch-analysis-ik-7.5.0.zip win file:///xxxx 外网开放 只改 0.0.0.0 会报错,需要配置下面两个参数,将服务器改成node https://juejin.im/post/5cb81bf4e51d4578c35e727d elasticsearch 降版本安装... rpm remove elasticsearch find / -name "*elasticsearch*" 删除所有出现的文件.不然安装低版本的会报错. rpm删除不干净导致日志部分报错 php barcode zbar epel wget https://download-ib01.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm rpm -Uvh epel-release-6-8.noarch.rpm yum install zbar-devel zbarimg xxx.png 返回解析后的内容 php win 下 执行命令时, 报出一个奇怪的错误8, 切换php版本到5.6就正常, 比较神奇...原因不明 ss5 所需扩展包 https://blog.csdn.net/Vincent95/article/details/71172986 thinkphp model update_time 被格式化问题 模型中,补充两个函数 setUpdateTimeAttr getUpdateTimeAttr 跳过系统自动格式化 php 线程安全和不安全会导致 ?undefined symbol:?core_globals 错误... https://www.jianshu.com/p/e13efb052844 百度图片搜索坑 图片尺寸1000 + jpg... 切记不要听沙雕说尺寸问题.....漏了另一个 mysql 关联更新 update a inner join b on a.id = b.id set a.xx=b.xxx where b.xx=xxx thinkphp 命令行模式 命令行模式一直找不到的问题 configure 动作中 setName 必需为小写且与类同名 php think xxx think 只是一个没有php后缀的php文件 rabbitmq php 后台运行 nohup php xxxx & 消费者消费后,标记消息 $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); php 多线程折腾起来麻烦事较多,通过 rabbitmq 做异步消息,并补充多个消费者...加快执行速度,相对来说改动和影响较小. recaptcha 人机交互验证 https://www.google.com/recaptcha/admin 注册.获取网站密钥和通讯密钥 页面demo:https://developers.google.com/recaptcha/docs/display 服务器demo:https://developers.google.com/recaptcha/docs/verify#api_request 国内使用 www.google.com 替换成 www.recaptcha.net 即可(js以及接口地址) webstorm 鼠标移动,代码提示开启 Setting->Editor->General->show quick documentation on mouse move uniapp h5 收录之神坑 #只会在客户端, 并不会传递到服务器端,导致路由参数无法传递到服务器端. 即使服务器端渲染出了静态页面, 也没用. 解决方案只能等官方的uniapp出正式的ssr 或者废了uniapp 用普通语言重写 所以官方说即使配置了关键字,二级页面也不会被收录,推荐走百度小程序.... 如果不是因为#后传递参数,还可以通过chrome headless 进行静态页面渲染 发个福利...chrome headless 获取页面渲染后文档结构... const content =await page.content(); 如果搞采集,还是不错的....vue.也可以采集和截图 tmux https://www.ruanyifeng.com/blog/2019/10/tmux.html tmux new -s 1 tmux ls 显示列表 ctrl b + w 显示列表并切换 ctrl b +c 新建 ctrl b + % 分屏 ctrl b + 上下左右 控制分屏 ctrl b + z 当前窗口最大化/恢复(第二次) ctrl b + x 关闭 附加功能,通过tmux打开,shell不会断线...哈哈... ss一键安装脚本国内安装问题... 没错,是要在国内装,做国内的代理. 打开安装脚本. 补充代理设置或者直接下载安装文件. 下载特别慢的. 手动下载,然后按照安装方式安装. puppeteer 报错 Chromium revision is not downloaded const browser = await puppeteer.launch({ //多加这个参数写死路径 executablePath: '/usr/bin/google-chrome' .... 服务器网页截图,拦截百度统计脚本... 特别需要注意截图的页面是否有出现死循环的问题...,同时为了优化速度,只保证打开指定域名的资源,其它资源全部屏蔽 await page.setRequestInterception(true); await page.on('request', request => { request.url()='' ; request.abort();/request.continue(); }) php 命令行情况下,久了mysql会出现has gone away问题 wait_timeout 默认为28800,也就是说,用久了.很容易出现掉链的问题. 反过来说,要测试也容易了,改小这个值.然后执行域名命令行.再访问数据库即可. thinkphp 数据库补充重连参数 'break_reconnect' => true, rabbitmq 消费报错时,延迟重新执行 消息报错时处理,多声明一个交换机和队列, 关键参数 x-message-ttl 超时时间 x-dead-letter-exchange 队列超时后,,进入的路由.设置原来队列的交换机即可 x-dead-letter-routing-key 下面同上 重新发送的队列的交换机记得不要和原队列相同. 原理是 声明两个通道,原消息队列和延迟发送的消息队列,一个消费者,消费消费报错时,将消息重新发送到延迟发送队列,超时触发,然后进入死信,也就是回到了原来的消息队列,这样形成一个反复执行的过程. 然后通过rabbitmq的管理平台手动进行调控. https://my.oschina.net/huaxian8812/blog/780086 elastcisearch 帐号密码访问 https://juejin.im/post/5c9c58fa5188250ef95d8b3c 配置 xpack.security.enabled: true xpack.security.transport.ssl.enabled: true postman中 http://user:pass@localhost:9200 sdk中同样是补充帐号密码前缀进行登陆 手机浏览器安装谷歌扩展 https://smartphones.gadgethacks.com/how-to/use-desktop-chrome-extensions-android-0196246/ 安装 kiwi 浏览器 打开 chrome://extentions 敏感词/敏感图片过滤 走微信免费接口 敏感图片, 没有做ocr处理, 色*图片会被检测出来, 但是敏感词截图, 会识别不出来... easywechat win 下图片上传报请求超时错误, 参数名补充@即可. linux下不会, 待核实具体问题点. mysql 主从复制 主机从机配置 https://www.jianshu.com/p/b0cf461451fb 从机延长同步时间参数 MASTER_DELAY=xxx 查了文档. mysql 8.0 才是每次传输时补充延迟, 5.7只在第一次延迟,后续都实时同步...,改成计划任务,通过计划任务控制,做延迟同步实现. 获取主机binlog当前位置. 然后从机开启, 等位置达到时,停止... 中文详细总结 https://www.fooher.com/20170502_53.html 官方配置参数列表 https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html 主从错误详细说明 http://xstarcd.github.io/wiki/MySQL/online_mysqlrepl_error.html ssh 稳定通道 autossh + 公钥登陆(不知道为啥ssh-copy-id不成功,我直接手动粘贴到/root/.ssh/authorized_keys 一行一个公钥, 记得得开启允许公钥登陆/etc/ssh/sshd_config 里) id_xx为默认私钥和公钥, 如果不是默认,需要通过-i 单独指定私钥 https://freeman.one/2018/12/08/ssh-tunnel/ mysql 误删除超快速恢复方案 利用主从实现数据库同步,再通过追加延迟参数,这样的数据库可以做为一个延迟备份数据库, 出现误删除操作时, 可以从这个比较慢同步的数据库中快速回复所需的数据. 恢复所需数据时,可以将商品id放入临时表中, 通过临时表,可以快速识别出丢失的数据然后进行恢复. 主从时, 主从复制时, 如果备份机或只读的那台. 没有什么特殊操作...空间比较小的时候. 可以考虑关闭binlog, 或者调整为2天. 默认是10天. 会导致生成的文件占用非常多的空间. editplus 搜索文件技巧 Search->Find in Files 包含的文件, 如果有多个的,用 ";" 进行分隔, 不需要搜索的, 也一样是通过";" 进行分隔. find 命令删除文件时, 如果文件包含空格或特殊字符导致的报错(rm: invalid option -- )处理 find . -name "*284x*" -print0 | xargs -0 rm -rf find 命令详解 https://wangchujiang.com/linux-command/c/find.html 共享目录报错提示 无法访问,你 中能没有权限访问网络资源 凭证管理器, 手动添加权限 https://blog.csdn.net/luman1991/article/details/71248761 sqlserver 默认访问,无法通过端口访问问题 需要在服务器开启监听全部,才能通过端口访问, 不清楚默认情况下走什么协议 sql server 网络配置-mssqlserver的协议>tcp/ip 是否启用, 属性中是否监听全部 查询当前所监听端口 exec sys.sp_readerrorlog 0, 1, 'listening' 正常需要看到5条记录,1434和1433 sa 登录需要在安全中设置为混合登录. nodejs 热更新 require 只会引入一次,必定是有缓存,同时是以绝对路径为缓存标志,路径分割是"\",win下如果出现拼接时,需要注意"/",需要替换成目标路径 检测文件是否被二次加载,只需要在加载文件中console.log即可 删除缓存 delete require.cache[pwd]; isntall 时报错 operation not permitted 需要删除lock文件 package.json 不存在, 只能npm init初始化... 不保留svn是原罪. navicat 12.1.x注册提示找不到rsa 文件都复制到目录, 最好删除原来的, 然后安装时路径多个加版本号. 以管理员权限运行注册机, 其它参考教程 egg-view-ejs 配置正确还报错:Can't find viewEngine 检查 cxt.render 前是否有加await. VMware 虚拟机无法复制 虚拟机设置,选项,客户机隔离,取消,再保存一遍
electron 收集整理
开发环境配置
本地示例文件
\meiqi\electron\meiqi_tools\
安装开发环境,配置后之后,可以F5直接运行,支持断点调试.
浏览器封装
接口暴露
考虑类似小程序的接入方式. 但是目前没想到需要封装的独立实现.(本地实现,基本可以考虑通过扩展)
扩展暂时没有发现,实在有需要,甚至可以考虑直接打包成jar然后通过electron调用jar实现.
网页/客户端通讯
ipcMain
ipcRenderer
可以直接在网页端,用nodejs的语法.
require类动作,为自动注入实现,需要开启 nodeIntegration: true
客户端/服务端通讯
是否考虑单页应用,然后浏览器完成全部工作? 把更新工作,全部交给web端
状态标志
用户登录,需要考虑状态保持的问题.或者每次登录都需要重新输入密码? 实现记住密码.
调试
web调试
可以通过直接触发(shift+ctrl+i)谷歌自带的调试,进行web-view部分的调试
客户端调试(菜单部分不可以隐藏)
主进程调试,通过vsc直接调试
菜单部分不可以隐藏,不然web-view部分的调试快捷键会无效(shift+ctrl+i).
考虑再另外注册调试动作.
热更新考虑,现在每次修改,都需要点击F5,可能无法再继续优化,web端可以考虑补充快捷键进行刷新
UI
暂时不考虑美化,直接打包,实现最基本的功能.
了解是否可以实现一套ui 实现 PC 和 手机端,减少工作量
考虑单页路由,实现快速和较好的用户体验,主要后面需要考虑上websocket, 需要考虑做成一个完整的app,需要通讯的保持
公司的页面一天到晚自己乱搞,改版,比较浪费时间,不方便写成单页应用, 或者自定义实现,相对来说,不是很显示.
或者考虑一套简单通用的单页应用,快速上手,然后让美工学习
打包
体积:普通情况下,打包大小接近150M+,需要经过压缩和精简处理(待测试了解)
打包教程:
经测试,不需要搞什么asar(暂时没有发现实际作用,后续需要vue类需要考虑)
直接压缩包:62M+
通过hm nis 打包大小:45M+
可以控制icon,开始菜单,卸载,二次安装还能选择是否覆盖等操作,其它相关
这样相对容易分发,但是更新需要再研究下,以及版本的相关控制.
代码干扰考虑
\resources\app\ 中直接包含了客户端的源码,防止别人修改后,发布,有一定危害(暂时没有靠谱的解决方案….甚至官方不提供支持…).
特殊功能考虑
打算封装一次,通过修改路径,让程序打开对应的网站实现相关功能. 不过只有打包的应用,安装时,才会出现图标,路径以及标题,说明这些.
cookie的读取和控制,还在折腾中.后续打算配置客户端,普通数据采集的验证码输入,直接让用户安装客户端或者直接提供谷歌扩展实现.相对来说.客户端会简单点.只需要做一步登录即可.
采集工具考虑.
获取指定的域名的cookie列表,如果不设置参数改为{},可以获取所有cookie内容.
断点阶段,会无法输出,应该是有远程调用,只有恢复浏览器活动时,才会触发then
require(‘electron’).session.defaultSession.cookies.get({ url: ‘xxx’ })
.then((cookies) => {
console.log(cookies);
}).catch((error) => {
console.log(error);
});
简单采集工具思路,主要针对普通采集,登录后,补充一个检测,登录成功,自动上传cookie内容到指定服务器,服务器通过该cookie进行数据采集
通过对url以及页面内容进行检测,检测到符合目标,就直接提示采集标志成功.如果发现失效,则直接跳转到登录页面.
只能针对小众东西,对有强自动化,各种验证的,再来~
自动更新
只有客户端需要更新,内容直接通过服务器访问,基本不需要更新,客户端更新,需要了解自带的版本检查流程相关.
目前内部使用的土方法
打包一次->压缩….
这个不适合更新
自动更新
需要注意特殊配置, 像数据库是否会被覆盖.
更新时,如果出现对数据库的修改,需要实现升级处理.
填坑
Jquery 找不到问题
npm 所有socket默认端口都是1080,即使关闭代理,也莫名奇妙会跑代理,一直报1080端口错误.shadowsock 默认端口是1080…如果出现,不是缓存问题…要注意.
c++混合开发
bug
自带notification 各种使用限制,环境的兼容问题.暂时通过node-notifier代替
修复node-notifier循环注册事件bug
需要考虑该种调用,是否关闭即无效.
本地存储
官方推荐
web 端有localStore类存储,不是很妥当,关键数据,还是考虑保存到本地数据库中.
实际环境可能出现的问题
断网, 断电, 程序损害时的迁移.
考虑让用户自行手动录入订单
爽爆的elasticsearch xpack sql
最近的一个场景:商品表到20w+ 属性表直接破了800w+(有个需要搜索其它的需求….导致80w的中间表不用,必需得跑全表,同时有个属性多字段匹配的功能.比如颜色,材质会出现复数字段.导致属性表较大,几乎有的没的属性都得上), 数据库单独移动到ssd,速度大概提升2-3倍, 下阶段还能通过将属性表按分类分表或者简单点直接用merge表引擎. 进行简单优化..
感觉都不如这个牛逼啊……
比较懒,写了个工具将sql自动转成elasticsql 只需要这段代码~
顺便做个广告:
搜索部分就是使用elasticsearch 同时支持图片搜索,属性搜索…
2019-07 至 2019-11 懒人笔记
tcpdump 监听指定端口和远程客户端 curl 指定目标服务器ip发起请求,用于测试内网和外网的访问,最近一个狗血护网,外网(机房)给堵了某些参数,通过监听和内外网分别请求证明服务器正常. 外网带特殊参数时, 完全监听不到目标客户端的请求 curl -x xxx:80 http://xxx -data 'xx=xx&xx=xx' 中间有反向代理,通过过滤响应头识别目标机器 tcpdump port \(80 or xxx\) and host \( xxxx or xxxx\) -vvvs 1500 -l -A | grep xxxx electron notification 官方的,使用限制颇多,win10下,老是碰到各种奇怪问题,通过node-notify代替 https://www.npmjs.com/package/node-notifier bug记录:node-notify on click 多次执行,会出现重复注册的问题.需要添加个标记变量修复. visual studio 6.0 稳定版本 6.0.9782.2 ...需要打sp6补丁... 不然有一堆神奇的bug php soapclient 代理 补充代理,重写__doRequest ,使用curl 构造函数调整修改为自动non-wsdl形式,wsdl形式为php底层实现,无法走socks5代理.会报错. ss5 防火墙冲突 icmp-host-prohibited 该句会导致后续开放端口失效 php session 锁 https://log.zvz.im/2016/02/27/PHP-session/ 切换成redis版本或者通过cache模拟session效果.不过防止session开启,需要跳过过滤器相关 session_write_close 主动关闭,注意关闭后session_status 是1, 是关闭状态,会导致后续$_SESSION类写入,无法写入文件保存,需要通过 session_start 开启,激活状态是2 tp 通过重写Session::set xxx相关操作. 实现自动关闭, 每次执行时, 程序会自动开启session. 记录一个session的诡异问题链接相同的时候.写入有效,但是读取无效.即使手动激活.一定要追加个参数区分开链接 linux 分区大小调整 fdisk -l 显示磁盘详细信息, 相关分区信息, 扩容的前提是磁盘本身够, 不够云主机类扩容, 或者加硬盘, 挂硬盘到大文件目录 df -h 检查分区使用情况(包含挂载情况) 需要检查清楚是否有别的挂载同时挂载到已经挂载的目录里,都需要从里到外依次卸载. 没有线程,依旧被占用,检查虚拟内存是否设置在目标分区中,需要关闭对应的虚拟内存 fuser -km /xxx 强制关闭目标分区所有进程 umount -f /xxx 卸载目标分区 可以直接卸载目标盘,也可以卸载目标挂载后的路径 通常是配制在/etc/fstab中,同时可以观察虚拟内存的设置 fdisk /dev/xxx 进入目标分区,按m根据提示进行相关操作 p 打印磁盘分区详细信息, 记录好分区当前开始位置. d 删除需要扩容的分区表信息 删除后,只是临时的,还未正式写入 n 设置新的分区信息,输入刚才记录好的分区起始位置和结束位置,如果希望全部,则将范围最事的数字输入即可,默认是原来的大小 w 写入分区信息 注意此时文件系统并未生效.需要通过 sudo e2fsck -af /dev/www sudo resize2fs /dev/www 分配文件系统大小 Table 'performance_schema.session_variables' doesn't exist 直接复制数据库到新版本mysql时出现 多种解决 mysql_upgrade -u root -p --force php excel 导出避免数字问题 $phpexcel->getActiveSheet()->getStyle('C' . $i)->getNumberFormat()->setFormatCode("@"); $phpexcel->getActiveSheet()->setCellValueExplicitByColumnAndRow ( 2, $i, $val['model'], 's' ); 两句需要同时加,否则msoffice有个奇怪点击后变回数字格式的问题. openoffice 不加第一句,点击时顶部值会显示一个" ' "(单引号) 富文本数组元素转字符串(string) flashfxp 传输文件被压缩成一行 修改传输模式为二进制,ascii 会丢失换行符号,比较奇怪 htaccess 设置帐号密码访问 AuthName "Restricted Area" AuthType Basic AuthUserFile /home/site/.htpasswd AuthGroupFile /dev/null require valid-user 生成密码:http://www.htaccesstools.com/htpasswd-generator/ myentunnel SwitchySharp 翻墙 服务器各种 相关参数开启 允许局域网 SOCKSPort=7077 改为 SOCKSPort=0.0.0.0:7077 ,即本地端口输入 : 0.0.0.0:8888 让局域网可以访问 linux 使用 socks, 安装 sock http proxy cover sock proxy polipo https://www.codevoila.com/post/16/convert-socks-proxy-to-http-proxy-using-polipo win https://tzrgaga.github.io/2017/04/12/forward-socks-by-privoxy/ forward-socks5 / 127.0.0.1:8888 . 最后有一个小点 mobaxterm 获取下载文件 settings->configuration 打开目录. 会得到这个路径. C:\Users\home\DOCUME~1\MobaXterm\slash\tmp\dragdrop sqlite3 ubuntu 安装依赖问题 sudo dpkg --purge --force-depends libsqlite3-0 sudo apt-get install libsqlite3-0 sudo apt-get install -f sudo apt-get install libsqlite3-dev sudo apt-get install sqlite3 tor epel 源 EPEL,即Extra Packages for Enterprise Linux的简称,是为企业级Linux提供的一组高质量的额外软件包 https://www.5xiaobo.com/?id=308 宝塔 ssl 证书识别异常 关闭重开,重新部署并不会修改目录位置,意思点击了let'sencrypt再点击宝塔自带的付费证书 php ?execution timed out terminating 修改php-fpm request_terminate_timeout ssl 端口转发 如果服务器某个端口被墙,又需要到,通过该方式进行连接 ssh -L 0.0.0.0:xxxx:localhost:xxxx root@xxxxx -p xxxx n2ray 你懂的~ 每次被搞,会的更多.... uniapp picker 对象数组问题. 傻逼东西....使用非常麻烦. php composer.phar install win 下安装. 最好先 php --version / 或 php -i 确认环境. 如果有多个版本的话 win10 文件复制到系统目录里后操作 access deny... 以前很沙雕复制出来修改再复制回去...发现只需要右键修改user权限.... npm config set proxy=socks://127.0.0.1:10808 npm config set https-proxy=socks://127.0.0.1:10808 https 走 http代理和部分软件走http代理,会出现一些莫名奇妙的问题,直socks代理则正常 win redis https://windows.php.net/downloads/pecl/releases/redis/ wss apache 配置 直接在: httpd.confg 后配置, 需要确保已经开启ssl这些 SSLProxyEngine on ProxyPass /wss ws://127.0.0.1:8282 ProxyPassReverse /wss ws://127.0.0.1:8282 php 通过 proxy 解析目标域名 curl_setopt($oCurl, CURLOPT_HEADER, 1); curl_setopt($oCurl, CURLOPT_HTTPPROXYTUNNEL, 1); thinkphp 奇怪页面缓存问题 html部分缓存没有即时更新.导致页面显示异常 websockets 重新链接 reconnecting-websocket.js ssh 通道阻塞 bitvise ssh client 比较好用,mytunel 容易因为阻塞断开链接 php 转化 webp 图片格式 https://www.php.net/manual/zh/function.imagecreatefromwebp.php 安装 webp支持 yum -y install libvpx-devel 编译命令补充(请根据版本): php 56 '--with-vpx-dir' '--with-webp-dir' 这句会识别不了 (new \Imagick($webp_file))->getImageMimeType() shell 尝试 反引号` 把命令中某个子命令替换为其执行的结果 svn 钩子 更新代码强覆盖处理 svn update --accept "theirs-conflict" #更新时,如果出现代码变动,以新更新的代码为主,进行合并,并不会覆盖,所以测试机如果有什么测试代码.记得删除...重新传 php 多透明图片透明细节 图片透明部分保持,不要丢失,每绘制一次,都会重新搞一次,针对对象(只对png类有效) imagealphablending($this->image, false); imagesavealpha($this->image, true); 强制指定具体某种颜色做为透明颜色 $white = imagecolorallocate($im, 0, 0, 0); imagecolortransparent($im,$white); 确保最后保存文件格式为png.....(这个要细心...避免沙雕问题...) wkhtmltopdf: cannot connect to X server 不要yum install wkhtmltopdf....直接rpm安装就正常,然后,如果原来用yum 安装,记得覆盖 /usr/bin/wkhtmltopdf 和 wkhtmltoimage两个没用的... vue 渲染截图问题 wkhtmltoimage,phantomjs都是无法操作的,会出现空白问题.只能用chrome headless+ puppeteer chrome centos 安装 https://www.cyberciti.biz/faq/howto-install-google-chrome-on-redhat-rhel-fedora-centos-linux/ puppeteer npm安装有点奇怪,通过yarn安装 await page.waitFor(500); //延迟返回,直接返回会导致加载慢,showMod改变加载速度,会导致加载非常慢 await page.goto(argv.url, {"waitUntil" : "networkidle0"}); //直至网页加载完成,加该句,可以不用加上面那句代码 php 命令行调用 npm 异常收集 环境变量 php 命令行用户是www, 会出现识别不了node 的问题.需要复制到/usr/bin/node或者直接完整路径 路径 require 时,同样会因为www用户,出现上述问题,可以require 时直接指定 权限问题 高权限用户调用命令时,会报错,但不会"卡死",www用户出现目录和文件权限问题时,会卡住,同时su 成 www 测试会相对直观 win 下快速检索目录 dir /s xxxx 指定目录检索匹配在in里的文件列出,应该实用到系统文件索引.速度非常快. 备用方案 everything es.exe 命令行接口,需要打开everything 检测程序运行命令: tasklist | findstr Everything.exe yum install *.rpm 直接安装rpm.... php 数据表达式计算+校验(非eval) EvalMath.class.php eval 异常捕获 在eval 前 set_error_handler ,异常级别 E_WARNING window.history.pushState 无刷新,修改url mysql 随机获取一条 select id,rand() r from table_name order r asc limit 1;//速度可以 libzip 版本不够 编译php 7.3 必需卸载libzip,再编译安装才有效,安装好后再去编译php7.3 补充--enable-zip https://segmentfault.com/a/1190000017570008 redis 访问频率限制 set key value expire ,访问时 inc 增加key expire不变. 过期会自动消息.达到频率限制的目的 宝塔奇怪cc问题 是通过将md5(ip+request), 疑似出现撞库,导致用户莫名奇妙拉黑
自考 高等数学学习工具
2019-07 半年份的懒人笔记
米波现场调整为 win 版本 不过win下,会牺牲不少性能,如果要求不高,可以考虑 修复微擎 在php 7.2 环境下登录异常 mbstring.fun_overload 导致各种字符串函数异常,导致session解密异常 php 7.2以上版本安装跳过ssl协议 pecl install channel://pecl.php.net/mcrypt-1.0.1 workerman 模拟压力测试脚本 httponly chrome 特性,只有返回时,标记为httponly,之后会无法在浏览器端读取以及修改.且发送的httponly不会再出出标记 nmap-vulners和vulscan https://www.4hou.com/technology/10481.html 需要走代理更新库 shell 使用 shadowsockets 局域网代理 curl 才能测试 ping 和 telnet 需要单独安装代理或shadowsock 服务器备份 svn 直接复制 passwd,authz mysql 复制数据库后,表空间要一起复制. 对源码做特殊修改,使include 不被open_basedir所限制, 新人测试环境,保护源码安全 php 源码 main/fopen_wrappers.c (代码部分是复制别人的...哈哈...省好多时间) 源码修改测试, 有 ./configure --disable-all 方便快速编译,只针对测试功能测试 PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC) php 源码编译安装 直接复制lnmp phpinfo中的编译参数,避免出错 collect2: ld returned 1 exit status make: *** [sapi/cli/php] Error 1 解决办法:make ZEND_EXTRA_LIBS='-liconv' ln -s /usr/local/lib/libiconv.so.2 /usr/lib64/ 独立php版本实现 需要另外指定编译目录,配置独立端口,配置独立 /tmp/phpxxx.sock,同时nginx也需要做同样处理,用alias会方便很多. alias 全局修改 /etc/bashrc (注意空格) samba 无法访问...可能没有权限...这句不关键,第二句才是关键.. Samba报错:不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接 sqlserver php https://www.jianshu.com/p/245b14583c47 只有7.0正常,7.2还不行. win 分钟级php类计划任务 php -q xxx.php cups https://www.cups.org/ "操作无法完成(错误 0X00000709)。再次检查打印机名称、并确保打印机连接网络" 识别不到打印机驱动问题. 可下载目标打印机驱动exe ,右键直接解压,再通过查找安装找到目标文件 xp识货无法安装的问题.只能随便添加一个打印机(此时无效),再直接修改注册表,改成目标打印机 打印机 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Print/Printers 打印驱动 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Print/Environments/Windows NT x86/Drivers apache vhost单独配置php配置参数 php_admin_value open_basedir xxx wkhtmltopdf 打印A4 隐藏头,隐藏尾(页面宽度750左右为A4常规大小) wkhtmltopdf -T 0 -L 0 -B 0 --page-size A4 --print-media-type --disable-smart-shrinking http://xxx xxx.pdf 中文分词 mysql 5.7 以上,同时设置ngram_token_size=2,200w内,一次like且文本不是很长的情况下,可以不考虑用like.多次like的情况下,10w起就需要用分词了,速度下降得比较厉害. mysql 一次命令复制表以及数据 CREATE TABLE igd_sysmsg_backup AS ( SELECT * FROM igd_sysmsg )
Qupload + 多种素材转换成图片实现 (psd,cdr,ai,png,jpg,gif,zip,rar,ppt,pptx,pdf,mp4,avi,mov)
ppt如果直接openoffice解析成pdf再转图片,会有错位问题. 暂时只发现可以通过python调用wps实现兼容的解析.
<?php class QuploadController extends CController { /** * 首页 */ protected function beforeAction($action) { return true; } function actionDemo() { $this->renderPartial ( 'demo' ); } function log($params = []) { write_log ( 'qupload', $params ); } function actionToken() { $u = lib ( 'qupload/base.php' ); $file_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $_GET ['name'] ); $file_format = strtolower ( $file_format ); if (! in_array ( $file_format, [ 'psd', 'cdr', 'ai', 'png', 'jpg', 'gif', 'zip', 'rar', 'ppt', 'pptx', 'pdf', 'mp4', 'avi', 'mov' ] )) { die ( 'do not allow the file format.' ); } $token = $u->genToken ( $_GET ['name'], $_GET ['size'], $_GET ['time'] ); if ($token) { if ($_GET ['size'] > 4 * 1000 * 1000 * 1000) { $result = json_encode ( array ( 'token' => $token, 'range' => implode ( '-', $u->getRange ( $token ) ), 'status' => $u->getStatus ( $token ), 'tmp_dir' => $u->getInterval (), 'status' => 2, 'error_msg' => '文件大小不能超过1G' ) ); $this->log ( $result ); echo $result; exit (); } $status = $u->getStatus ( $token ); if ($status === 2) { // 上传成功 try { list ( $images_list, $file_info ) = $this->parseImglist ( $u->getInterval () . '/' . $token ); $error_msg = "success"; } catch ( Exception $e ) { $error_msg = $e->getMessage (); } } $result = json_encode ( array ( 'token' => $token, 'range' => implode ( '-', $u->getRange ( $token ) ), 'status' => $u->getStatus ( $token ), 'tmp_dir' => $u->getInterval (), 'error_msg' => $error_msg, 'images_list' => $images_list ) ); $this->log ( $result ); echo $result; exit (); } } public static function uploadDir() { return UPLOAD_DIR . 'file/'; } public static function uploadUrl() { return UPLOAD_URL . 'file/'; } function actionDownload() { // 弃用,上传成功,根据文件路径,将文件重命名 // 上传时,需要检测当前用户是否登录. // exit (); // $_REQUEST ['file'] = '17625/3344abdedd12a65dc7f78d991338d95c4558f66f'; // $_REQUEST ['file'] = '17625/050f98d89455c3e6e4c6f96f8d1f7dc8b43b5e70'; $file = urldecode ( $_REQUEST ['file'] ); if (! preg_match ( '/^\/?\d+\/\w+\.\w+$/ims', $file )) { die ( 'the filename is error' ); } $filePath = UPLOAD_DIR . 'file/' . $file; if (! file_exists ( $filePath )) { die ( 'the file do not exists' ); } if (! file_exists ( $filePath . '.json' )) { die ( 'the file json do not exists' ); } $file_info = file_get_contents ( $filePath . '.json' ); $file_info = json_decode ( $file_info, true ); if (! $file_info) { die ( 'the file json is error' ); } $filename = $file_info ['name']; header ( 'Content-Description: File Transfer' ); header ( 'Content-Type: application/octet-stream' ); $ua = $_SERVER ["HTTP_USER_AGENT"]; if (preg_match ( '/MSIE/', $ua )) { header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $filename ) . '"' ); } elseif (preg_match ( "/Firefox/", $ua )) { header ( 'Content-Disposition: attachment; filename*="utf8\'\'' . $filename . '"' ); } else { header ( 'Content-Disposition: attachment; filename="' . $filename . '"' ); } header ( 'Content-Transfer-Encoding: binary' ); header ( 'Expires: 0' ); header ( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' ); header ( 'Pragma: public' ); header ( 'Content-Length: ' . filesize ( $filePath ) ); set_time_limit ( 300 ); // 避免下载超时 ob_end_clean (); // 避免大文件导致超过 memory_limit 限制 readfile ( $filePath ); } public function actionFetch_img_list() { $u = lib ( 'qupload/base.php' ); $token = $_REQUEST ['token']; $status = $u->getStatus ( $token ); if ($status == 2) { $error_msg = 'success'; } else { $error_msg = 'error'; } $img_dir = Article::uploadFileDir () . $u->getInterval () . '/' . $token . '_img/'; $img_list = FileTools::getCurList ( $img_dir, array ( '/\.(png|jpg|gif)$/i' => false ) ); if (empty ( $img_list )) { throw new Exception ( "[{$token}]获取图片异常" ); } // ppt 解析出所有图片 $images_list = [ ]; $dir = str_replace ( '\\', '/', WWW_DIR ); foreach ( $img_list as $img ) { $img = str_replace ( '\\', '/', $img ); $img = str_replace ( $dir, '', $img ); $images_list [] = $img; } echo json_encode ( array ( 'token' => $token, 'range' => implode ( '-', $u->getRange ( $token ) ), 'status' => $status, 'tmp_dir' => $u->getInterval (), 'error_msg' => $error_msg, 'images_list' => $images_list ) ); } function actionUpload() { set_time_limit ( 0 ); ignore_user_abort ( true ); $u = lib ( 'qupload/base.php' ); $token = isset ( $_GET ['token'] ) ? $_GET ['token'] : null; $start = isset ( $_GET ['start'] ) ? ( int ) $_GET ['start'] : null; if ($start > 4 * 1000 * 1000 * 1000) { $msg = 'do not allow the size more than 1GB'; $this->log ( [ $msg, $_GET ] ); die ( $msg ); } if (! $info = $u->getTokenInfo ( $token )) { $msg = 'can not get the token info'; $this->log ( [ $msg, $_GET ] ); die ( $msg ); } $fp = fopen ( 'php://input', 'r' ); $size = $u->size ( $token ); // 如果上传起始位置大于文件位置 肯定失败 if ($start > $size) { $this->log ( [ 'position is wrong', $_GET ] ); exit ( 0 ); } // 如果文件已经存在 那么肯定是添加 if ($size > 0) { $u->append ( $token, $fp, $start ); } else { $u->store ( $token, $fp ); } $status = $u->getStatus ( $token ); $images_list = [ ]; if ($status === 3) { $u->delete ( $token ); // 上传出错,清理标记,重新上传 $status = 0; } elseif ($status === 2) { // 上传成功 try { list ( $images_list, $file_info ) = $this->parseImglist ( $u->getInterval () . '/' . $token ); $error_msg = "success"; } catch ( Exception $e ) { $error_msg = $e->getMessage (); } } else { // status=1 // 上传中 } $result = json_encode ( array ( 'range' => implode ( '-', $u->getRange ( $token ) ), 'status' => $status, 'tmp_dir' => $u->getInterval (), 'token' => $token, 'error_msg' => $error_msg, 'images_list' => $images_list ) ); $this->log ( $result ); echo $result; } function parseImglist($upload_file) { $file_path = UPLOAD_DIR . '/file/' . $upload_file; if (! file_exists ( $file_path )) { sjson ( false, $title, '文件不存在' ); } $file_info = file_get_contents ( $file_path . '.json' ); $file_info = json_decode ( $file_info, true ); if (! $file_info) { sjson ( false, $title, '文件信息异常' ); } $file_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $file_info ['name'] ); $file_format = strtolower ( $file_format ); $file_flag = preg_replace ( '/^\w+\/(\w+\.\w+)$/ims', '\\1', $upload_file ); $file_path_with_format = $file_path . $file_format; // 如果为图片,直接复制图片到指定目录 $img_dir = $file_path . '_img'; if (file_exists ( $img_dir )) { lib ( 'FileSystem.php' ); // 如果目录已经存在,删除,再进行命令调用. // 不然容易出现是否覆盖的提示,导致脚本异常 rmdirs ( $img_dir ); } mkdir ( $img_dir, 0777 ); // 复制文件,并做解析处理,如果防止出现异常时,需要进行二次提交 if (in_array ( $file_format, [ 'psd', 'cdr', 'ai', 'png', 'jpg', 'gif', 'zip', 'rar' ] )) { // 只有图片允许压缩包 // psd,cdr,ai,png,jpg,gif,zip // sjson ( false, $title, '平面设计只允许上传psd,cdr,ai,png,jpg,gif,zip<br>不能' . $file_format ); if (in_array ( $file_format, [ 'zip', 'rar' ] )) { // 先获取压缩包文件内容,检查格式是否包含在指定列表中 if ($file_format == 'zip') { $command = "unzip -l {$file_path}"; } else { $command = "unrar v {$file_path}"; } list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]压缩包内容获取异常" ); // 不管压缩包格式是rar 还是 zip 获取内容列表时,都是 // ----- // .... // --------- $begin_file_content = 0; foreach ( $output as $line ) { if (stristr ( $line, '----' )) { $begin_file_content ++; continue; } if ($begin_file_content == 1) { $img_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $line ); if (! in_array ( $img_format, [ 'psd', 'cdr', 'ai', 'png', 'jpg', 'gif' ] )) { throw new Exception ( "[{$upload_file}]压缩包文件内容格式异常." ); } } elseif ($begin_file_content == 2) { break; } } if ($file_format == 'zip') { $command = "unzip {$file_path} -d {$img_dir}"; } else { $command = "unrar x {$file_path} {$img_dir}"; } list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]压缩包解压异常" ); $file_list = FileTools::getCurList ( $img_dir, array ( '/\.(png|jpg|gif|ai|cdr|psd)$/i' => false ) ); if (empty ( $file_list )) { throw new Exception ( "[{$upload_file}]获取压缩包内容异常" ); } // ppt 解析出所有图片 $images_list = [ ]; foreach ( $file_list as $file ) { $format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $file ); if (in_array ( $format, [ 'psd', 'ai' ] )) { // 通过命令将文件转成图片 $command = "convert {$file}[0] {$file}.png"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]图片[{$format}]转化异常" ); $file = $file . '.png'; } elseif (in_array ( $format, [ 'cdr' ] )) { // cdr 格式比较特殊 // 通过命令将文件转成图片 $_pdf_file = "{$file}.pdf"; $command = "uniconvertor {$file} {$_pdf_file}"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf异常" ); $command = "convert {$_pdf_file} {$_pdf_file}.png"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf解析成图片异常" ); $file = $_pdf_file . '.png'; } $images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $file ); } } elseif (in_array ( $file_format, [ 'png', 'jpg', 'gif' ] )) { // 如果为图片,直接复制图片到指定目录 $img_path = $img_dir . '/' . $file_flag; copy ( $file_path, $img_path ); $images_list [] = Article::uploadFileUrl () . $upload_file . '_img/' . $file_flag; } elseif (in_array ( $file_format, [ 'psd', 'ai' ] )) { // 通过命令将文件转成图片 $img_path = $upload_file . '_img/' . $file_flag . '.png'; $command = "convert {$file_path}[0] " . Article::uploadFileDir () . $img_path; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]图片转化异常" ); $images_list [] = Article::uploadFileUrl () . $img_path; } elseif (in_array ( $file_format, [ 'cdr' ] )) { // cdr 格式比较特殊 // 通过命令将文件转成图片 $_pdf_file = "{$img_dir}{$file_flag}.pdf"; $command = "uniconvertor {$file_path} {$_pdf_file}"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf异常" ); $command = "convert {$_pdf_file} {$_pdf_file}.png"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf解析成图片异常" ); $images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', "{$_pdf_file}.png" ); } } elseif (in_array ( $file_format, [ 'ppt', 'pptx' ] )) { $jod_dir = EXT_DIR . '/convert_tools/'; // sjson ( false, $title, 'PPT模板只允许上传ppt,pptx' ); $command = "convert {$file_path}[0] " . Article::uploadFileDir () . $img_path; $pdf_file = $img_dir . '/' . $file_flag . '.pdf'; // ppt to pdf $command = "java -jar {$jod_dir}jodconverter/lib/jodconverter-cli-2.2.2.jar {$file_path} {$pdf_file}"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]ppt转pdf异常" ); // pdf to png $command = "convert {$pdf_file} {$pdf_file}.png"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]pdf转图片异常" ); $img_list = FileTools::getCurList ( $img_dir, array ( '/\.png$/i' => false ) ); if (empty ( $img_list )) { throw new Exception ( "[{$upload_file}]获取图片异常" ); } // ppt 解析出所有图片 $images_list = [ ]; foreach ( $img_list as $img ) { $images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img ); } } elseif (in_array ( $file_format, [ 'pdf' ] )) { // pdf to png $command = "convert {$file_path} {$img_dir}/{$file_flag}.png"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]pdf转图片异常" ); $img_list = FileTools::getCurList ( $img_dir, array ( '/\.png$/i' => false ) ); if (empty ( $img_list )) { throw new Exception ( "[{$upload_file}]获取图片异常" ); } // ppt 解析出所有图片 $images_list = [ ]; foreach ( $img_list as $img ) { $images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img ); } } else if (in_array ( $file_format, [ 'mp4', 'avi', 'mov' ] )) { // 视频 mp4,avi,mov // sjson ( false, $title, '视频编辑只允许上传mp4,avi,mov' ); // 获取视频总长度 $file_length = $this->getVideoLength ( $file_path ); if ($file_length < 10) { // 视频长度小于10秒,只获取一张图片, $file_img_spans = [ 0 ]; } else { // 每10分之一长度的地方进行图片采集 $file_img_span = intval ( $file_length / 10 ); while ( ($left_file_length += $file_img_span) <= $file_length ) { $file_img_spans [] = $left_file_length; } } foreach ( $file_img_spans as $time_span ) { $img = "{$img_dir}/{$file_flag}-{$time_span}.png"; // 小于0.1容易生成空白图片,提示缓存异常啥的 $command = "ffmpeg -ss {$time_span} -t 0.1 -i {$file_path} -y -f mjpeg {$img}"; list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]视频提取图片[{$time_span}]异常" ); } $img_list = FileTools::getCurList ( $img_dir, array ( '/\.png$/i' => false ) ); if (empty ( $img_list )) { throw new Exception ( "[{$upload_file}]获取图片异常" ); } // ppt 解析出所有图片 $images_list = [ ]; foreach ( $img_list as $img ) { chmod ( $img, 0777 ); $images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img ); } } else { die ( "[{$upload_file}]上传文件格式异常" ); } return [ $images_list, $file_info ]; } /** * * @param unknown $command * @return string[ $val,//异常状态标记,通常0表示正常 * $output,//命令执行结果 * $result//最后一句命令返回 * ] */ function runCommand($command, $msg = '') { $output = ""; $val = ""; // $val=0 表示正常 // 需要标记 ' 2>&1' 将屏幕输出内容(包括异常)全部进行反馈,不加会返回空 $result = exec ( $command . ' 2>&1', $output, $val ); if ($val) { // gddebug ( $command, $val, $output, $result ); write_log ( 'runcommand', [ $command, $val, $output, $result ] ); throw new Exception ( $msg ); } return [ $val, $output, $result ]; } function getVideoLength($file) { if (! file_exists ( $file )) { throw new Exception ( "file do not exists" ); } $dir = dirname ( $file ); $command = "ffmpeg -i {$file} 2>&1"; $output = ""; $val = ""; $result = exec ( $command, $output, $val ); foreach ( $output as $line ) { if (preg_match ( '/Duration\: (\d+(?:\.\d+)?):(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)\, start/ims', $line, $match )) { $second = intval ( $match [1] ) * 60 * 60 + intval ( $match [2] ) * 60 + intval ( $match [3] ); return $second; } } throw new Exception ( "识别不到视频,请检查 result 变量内容" ); } function actionIndex() { exit (); } }
任意多边形坐标范围判断(重写自百度地图js文件)
<?php /** * * @author Administrator * */ class MapTools { public static function model() { return new MapTools (); } /** * 判断点是否在矩形内 * * @param * {Point} point 点对象 * @param * {Bounds} bounds 矩形边界对象 * @return s {Boolean} 点在矩形内返回true,否则返回false */ function isPointInRect($point, $bounds) { // 西南脚点 $sw = $bounds ['sw']; // 东北脚点 $ne = $bounds ['ne']; return ($point ['lng'] >= $sw ['lng'] && $point ['lat'] >= $sw ['lat'] && $point ['lng'] <= $ne ['lng'] && $point ['lat'] <= $ne ['lat']); } /** * 返回一个多边形的最小外矩形(西南点和东北点的坐标) * * @param array $polygon */ function getBoundsByPolygon($polygon) { $least_lng = $polygon [0] ['lng']; $least_lat = $polygon [0] ['lat']; $largest_lng = $polygon [0] ['lng']; $largest_lat = $polygon [0] ['lat']; foreach ( $polygon as $point ) { if ($point ['lng'] < $least_lng) { $least_lng = $point ['lng']; } if ($point ['lat'] < $least_lat) { $least_lat = $point ['lat']; } if ($point ['lng'] > $largest_lng) { $largest_lng = $point ['lng']; } if ($point ['lat'] > $largest_lat) { $largest_lat = $point ['lat']; } } return array ( 'sw' => array ( 'lng' => $least_lng, 'lat' => $least_lat ), 'ne' => array ( 'lng' => $largest_lng, 'lat' => $largest_lat ) ); } /** * 判断点是否在多边形内 * * @param unknown_type $point * @param unknown_type $polygon * @return boolean */ function isPointInPolygon($point, $polygon) { if (empty ( $polygon )) { return false; } // 首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false // 可以通过绘制矩形时,补充多边形的中心进行计算处理. $polygonBounds = $this->getBoundsByPolygon ( $polygon ); if (! $this->isPointInRect ( $point, $polygonBounds )) { return false; } $pts = $polygon; // 获取多边形点 // 下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改 // 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则 // 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。 $N = count ( $pts ); $boundOrVertex = true; // 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true $intersectCount = 0; // cross points count of x $precision = 2e-10; // 浮点类型计算时候与0比较时候的容差 $p1 = $p2 = null; // neighbour bound vertices $p = $point; // 测试点 $p1 = $pts [0]; // left vertex for($i = 1; $i <= $N; ++ $i) { // check all rays if ($p ['lng'] == $p1 ['lng'] && $p ['lat'] == $p1 ['lat']) { return $boundOrVertex; // p is an vertex } $p2 = $pts [$i % $N]; // right vertex if ($p ['lat'] < min ( $p1 ['lat'], $p2 ['lat'] ) || $p ['lat'] > max ( $p1 ['lat'], $p2 ['lat'] )) { // ray // is // outside // of // our // interests $p1 = $p2; continue; // next ray left point } if ($p ['lat'] > min ( $p1 ['lat'], $p2 ['lat'] ) && $p ['lat'] < max ( $p1 ['lat'], $p2 ['lat'] )) { // ray // is // crossing // over // by // the // algorithm // (common // part // of) if ($p ['lng'] <= max ( $p1 ['lng'], $p2 ['lng'] )) { // x is before of // ray if ($p1 ['lat'] == $p2 ['lat'] && $p ['lng'] >= min ( $p1 ['lng'], $p2 ['lng'] )) { // overlies // on // a // horizontal // ray return $boundOrVertex; } if ($p1 ['lng'] == $p2 ['lng']) { // ray is vertical if ($p1 ['lng'] == $p ['lng']) { // overlies on a vertical ray return $boundOrVertex; } else { // before ray ++ $intersectCount; } } else { // cross point on the left side $xinters = ($p ['lat'] - $p1 ['lat']) * ($p2 ['lng'] - $p1 ['lng']) / ($p2 ['lat'] - $p1 ['lat']) + $p1 ['lng']; // cross // point // of // lng if (abs ( $p ['lng'] - $xinters ) < $precision) { // overlies on // a ray return $boundOrVertex; } if ($p ['lng'] < $xinters) { // before ray ++ $intersectCount; } } } } else { // special case when ray is crossing through the vertex if ($p ['lat'] == $p2 ['lat'] && $p ['lng'] <= $p2 ['lng']) { // p crossing // over // p2 $p3 = $pts [($i + 1) % $N]; // next vertex if ($p ['lat'] >= min ( $p1 ['lat'], $p3 ['lat'] ) && $p ['lat'] <= max ( $p1 ['lat'], $p3 ['lat'] )) { // p.lat // lies // between // p1.lat // & // p3.lat ++ $intersectCount; } else { $intersectCount += 2; } } } $p1 = $p2; // next ray left point } if ($intersectCount % 2 == 0) { // 偶数在多边形外 return false; } else { // 奇数在多边形内 return true; } } /** * 签名校验 * * @param unknown $ak * @param unknown $sk * @param unknown $url * @param unknown $querystring_arrays * @param string $method * @return string */ function caculateAKSN($ak, $sk, $url, $querystring_arrays, $method = 'GET') { if ($method === 'POST') { ksort ( $querystring_arrays ); } $querystring = http_build_query ( $querystring_arrays ); return md5 ( urlencode ( $url . '?' . $querystring . $sk ) ); } /** * 通过地址返回坐标 * * @param string $address_name * @return mixed|boolean */ function getLocationByAddress($address_name) { // API控制台申请得到的ak(此处ak值仅供验证参考使用) $ak = 'xxxxxxxxxxxxxx'; // 应用类型为for server, 请求校验方式为sn校验方式时,系统会自动生成sk,可以在应用配置-设置中选择Security // Key显示进行查看(此处sk值仅供验证参考使用) $sk = 'xxxxxxxxxxxxxxxxx'; // 以Geocoding服务为例,地理编码的请求url,参数待填 $url = "http://api.map.baidu.com/geocoder/v2/?address=%s&output=%s&ak=%s&sn=%s"; // get请求uri前缀 $uri = '/place/v2/search'; // 地理编码的请求output参数 $output = 'json'; $pois = '0'; $query = $address_name; $region = '全国'; $scope = 1; $querystring_arrays = array ( 'query' => $query, 'output' => $output, 'ak' => $ak, 'scope' => $scope, 'region' => $region ); $url = "http://api.map.baidu.com/place/v2/search?query=%s&output=%s&ak=%s&sn=%s&scope=%s®ion=%s"; // 调用sn计算函数,默认get请求 $sn = $this->caculateAKSN ( $ak, $sk, $uri, $querystring_arrays ); // 请求参数中有中文、特殊字符等需要进行urlencode,确保请求串与sn对应 $target = sprintf ( $url, urlencode ( $query ), $output, $ak, $sn, $scope, urlencode ( $region ) ); // 输出计算得到的sn // echo "sn: $sn \n"; // 输出完整请求的url(仅供参考验证,故不能正常访问服务) // echo "url: $target \n"; $content = file_get_contents ( $target ); $content = json_decode ( $content, true ); if ($content ['status'] == 0 && isset ( $content ['results'] ) && ! empty ( $content ['results'] ) && isset ( $content ['results'] [0] ['location'] ) && ! empty ( $content ['results'] [0] ['location'] )) { // var_export ( $content ); return $content ['results'] [0] ['location']; } return false; } public function getAddressByLocation($longitude, $latitude) { $url = "http://api.map.baidu.com/geocoder/v2/?ak=thT0201pFtgt3VApRT2mhlA6mmpQUnWQ&location={$latitude},{$longitude}&output=json"; $ch = curl_init ( $url ); curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true ); // 获取数据返回 curl_setopt ( $ch, CURLOPT_BINARYTRANSFER, true ); // 在启用 // CURLOPT_RETURNTRANSFER // 时候将获取数据返回 $output = curl_exec ( $ch ); $arr = json_decode ( $output, true ); if (! $arr || $arr ['status'] || ! isset ( $arr ['result'] )) return false; return $arr ['result'] ['formatted_address']; } }
workerman 业务并发测试
<?php use \Workerman\Worker; use \Workerman\WebServer; use \GatewayWorker\Gateway; use \GatewayWorker\BusinessWorker; use \Workerman\Autoloader; use \Workerman\Connection\AsyncTcpConnection; use \Workerman\Lib\Timer; require_once './vendor/autoload.php'; $worker = new Worker (); $worker->onWorkerStart = 'connect'; function connect() { // 2000个链接 // if ($count ++ >= 400) { // return; // } for($count = 0; $count < 400; $count ++) { $func = function ($count) { $session_id = 'd34pkfuf88vom9ugptop7e5' . $count; // 建立异步链接 // $con = new AsyncTcpConnection ( 'ws://xxxx:8282' ); $con = new AsyncTcpConnection ( 'ws://xxxxx:8282' ); $con->onMessage = function ($con, $msg) use ($session_id, $count) { // 服务器消息推送 echo "recv $session_id $msg\n"; $msg = json_decode ( $msg, true ); if ($msg && isset ( $msg ['action'] )) { switch ($msg ['action']) { case 'broadcast' : $time = ( int ) date ( 'dHis', time () ); $con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"client_time\",\"value_value\":{$time}}" ); break; } } }; $con->onClose = function ($con) use ($session_id, $count) { echo "con $session_id close\n"; }; $con->onConnect = function ($con) use ($session_id, $count) { // $con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"score\",\"value_value\":{$val}}" ); // 模拟登录动作 $con->send ( "{\"model\":\"remote\",\"action\":\"msg\",\"remote_url\":\"http://xxxxxx/workerman_remote/do/\",\"params\":{\"model\":\"user\",\"action\":\"login\",\"rm_session_id\":\"{$session_id}\"}}" ); // 当前链接每10秒发个心跳包 $time = ( int ) date ( 'dHis', time () ); $con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"client_time\",\"value_value\":{$time}}" ); Timer::add ( 10, function () use ($con, $session_id, $count, $time) { // 每10秒推送一个消息 // $con->send ( "{\"model\":\"user\",\"action\":\"sys_time\",\"session_id\":\"{$session_id}\",\"client_time\":\"{$time}\"}" ); // 模拟数钱,一秒推送10个消息 for($i = 0; $i < 10; $i ++) { $val = ( int ) date ( 'dHis', time () ); $con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"score\",\"value_value\":{$val}}" ); } echo $count . " send complete\n"; } ); }; $con->connect (); echo $count . " connections complete\n"; }; $func ( $count ); } } Worker::runAll ();
在400条的情况下,内存用不了多少,稳定性也有保证,不过实际情况,还是得具体场景才能判断.
小程序web-view开发框架(写给公司的特殊形式,只做说明,不含代码~.~)
需要先了解手机软件开发有三种形式,原生,混合开发,React-Native开发:
原生开发就是使用手机所支持的原生语言进行开发,像android是用java,iphone是 Objective-C
混合开发就是内置web-view形式,通过在web-view中注入一个js对象,并提供原生语言接口,js通过调用这些接口,实现网页应用和手机间的交互,像phonegap,dcloud等,
React-Native 开发则是直接创建第三种语言,类似html和js,然后生成对应的手机软件
三者主要对比性能和开发便捷程度
原生速度最快,但是跨平台性限制,导致各平台,都需要开发对应版本, 所以开发速度肯定是最慢的.
混合开发,各平台提供统一的接口给注入的js对象调用,所以只需要掌握基本的html和js基础,熟悉注入对象的文档即可开发能在多平台上跑的应用.速度最快,但是性能是最差的.因为是通过web-view进行渲染,性能是个硬伤,但是mui等前端框架,可极大程度的缩小差距. 但是混合应用, 说到底,只能开发展示类的应用.
React-Native 则是编写完成后,编辑器会编译成对应平台的软件,性能相对来说比混合好,开发也相对中等.
小程序则是比较接近react-native的形式,但是又可通过web-view的处理形式实现混合开发,公司这边使用的是web-view形式,上手相对容易很多.
小程序的限制:
小程序的限制是底层权限没有开放,限制比较高,导致写出来的应用,实际上是各公众号的功能差不多.在网速正常情况下,二者功能基本没有太大差别.唯一的优势基本就是入口可以在顶部下来访问.但是最大的缺点则是无法自由的进行模板消息推送. 小程序只有提交表单的情况下,会得到一个表单id,通过该表单才可以进行小程序消息下发,所以小程序一但离线,基本无法通知用户, 只能做为一款极其轻量的应用. 不过在绑定的前提下,公众号可以代替小程序推送消息,并通过消息访问小程序,前提是关注. 做到这步非常麻烦.小程序返回公众号流程繁琐,无法直接进入公众号.
公众号的类型:订阅号,服务号,小程序,企业号…
各公众号openid相互独立,所以,小程序用户的openid不同于其它公众号,且无法共享
公司web-view形式的小程序开发:
微信开发者工具
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
框架目录结构,每种必需小程序的功能,单独写成一个独立页面,然后通过wx进行访问
小程序开发者工具截图
小程序获取用户信息不同于公众号授权跳转,进入的时候就强制授权获取,且只能获取一次.且返回的是用户的详细信息和一个code,通过该code,才能获取小程序的用户openid. 所以入口处将用户信息传递到服务器端,必须由服务器进行保存,如果丢失,二次访问时,像头像,昵称这些信息将无法获取.除非将对应服务号接入,通过服务号进行授权跳转(小程序web-view本质是个浏览器,只是限制了长按扫描二维码,但是授权跳转有效)
小程序由于分享,支付,客服,关注,登录等特殊动作,必须由纯小程序代码进行编写,所以当需要触发上述功能时,web-view通过内置对象,携带参数并跳转到对应的页面,操作完成后,进行返回.以上为公司小程序的基本实现形式.
特殊模板小程序开发
通过开发一套完整的小程序,并提交到开放平台,绑定做为通用小程序模板, 这样只需要用户给对应的平台授权,平台可以直接给对应用户生成一套专属的小程序. 通过配置扩展变量,标记当前是哪个用户使用该模板.
web-view的最大优势为修改在web端,无需像小程序一般提交修改审核,极其便利.同时,在确保公众号开发一套任意的功能,通过关联openid,即可无缝整合成小程序访问.
最近小程序的优化,一个是通过头部跳转 Location: xxx 跳转”实现”不跳转太多次进入页面