├── .gitignore ├── README.md ├── PHP ├── 老项目的迁移手记.md ├── 冷门PHP函数汇总.md ├── PHP GD库解析一张简单图片并输出.md ├── 运行调试你的PHP代码.md ├── PHP程序员必备工具.md ├── PHP程序员必看书籍.md ├── PHP程序员必须知道的两种日志.md ├── PHP程序员如何优雅的搬砖.md ├── PHP程序员如何简单的开展服务治理架构(二).md ├── PHP程序员如何简单的开展服务治理架构(一).md ├── Swoole难上手?从EasySwoole开始.md ├── PHP使用Oracle数据库的准备工作.md ├── PHP程序员如何简单的开展服务治理架构(三).md ├── 老项目重构手记之用户系统.md ├── 一道看似简单的面试题.md ├── 2019 PHP程序员发展路线.md ├── 来!狂撸一款PHP现代化框架 (准备工作).md ├── 不一样的PHP基础知识汇总.md ├── 举枪消灭"烂代码"的实战案例.md ├── 取代PHP原生函数的一些扩展包.md ├── 2020 PHP程序员修炼秘籍.md ├── 使用GrumPHP来纠正代码“毛病”.md └── 来!狂撸一款PHP现代化框架 (路由的设计).md ├── 不要被集成环境束缚住你前进的脚步.md ├── “生于忧患,死于安乐”之程序员人生.md ├── 基于业务设计数据表的总结.md ├── 程序员自省录.md ├── Go ├── PHP To Go 转型手记 (一).md ├── PHP To Go 转型手记 (三).md ├── PHP To Go 转型手记 (二).md ├── PHP To Go 转型手记 (终).md └── Beego 文件上传至七牛云的玩法.md ├── 浅谈架构是为了什么 (上).md ├── Laravel ├── 你可能需要了解下Laravel集合.md ├── Laravel5.4队列简单配置与使用.md ├── Laravel-Action 对代码的改造.md ├── Laravel源码解析之从入口开始.md ├── Laravel源码解析之Model.md └── Laravel源码解析之反射的使用.md ├── 八年以后,我选择了创业.md ├── Shop ├── 电商系统设计之商品[番外篇].md ├── [还魂篇] 初来乍到如何致人于死地.md ├── 电商系统设计之购物车.md ├── 电商系统设计之商品 (中).md ├── 通用系统设计之优惠卷.md ├── 电商系统设计之商品接口.md └── 电商系统设计之用户系统.md ├── Nginx ├── NGINX日志配置总结.md ├── 安装Nginx要从娃娃抓起.md └── GitLab搭建并接入自建Nginx.md ├── MySQL ├── MySQL SQL模式特点汇总.md ├── MySQL常用函数汇总.md └── MySQL常用系统表汇总.md ├── 浅谈重构造成的灾难性毁灭.md ├── 是时候了解下Travis CI是什么了.md ├── 用MAC还安装集成环境可就OUT喽.md ├── RabbitMQ 初体验.md ├── Supervisor 从入门到放弃.md ├── 浅谈架构是为了什么 (下).md ├── Docker ├── 五分钟快速了解Docker.md └── Docker构建程序员的日常.md ├── 论某教育机构考试系统设计.md ├── 优化你的PHP代码,从现在做起.md ├── 我与Jetbrains的这些年.md └── 日常划水:短信验证码开发实例.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ## About Me 4 | Hi, I am CrazyCodes, born in 1996. I am a PHP development engineer. 5 | 6 | 你好,我是CrazyCodes,出生在1996年。我是一名PHP开发工程师。 7 | 8 | My whole life article will be put here and my blog, I hope every line of code, every text can help you. Although not a big god, but I have been in the pursuit of the big god on the road 9 | 10 | 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。虽然不是大神,但我一直在追寻大神的路上 11 | 12 | ## Url 13 | The following is a list of all the addresses where I can be found 14 | 15 | 这里列出可以找到我的所有地址 16 | 17 | - segmentfault https://segmentfault.com/u/crazycodes 18 | - blog https://blog.fastrun.cn 19 | - 掘金 https://juejin.im/user/5b4eb3856fb9a04fdd7d516b 20 | - Email crazycodes@yeah.net 21 | - QQ 919342864 22 | - 微信 php365t -------------------------------------------------------------------------------- /PHP/老项目的迁移手记.md: -------------------------------------------------------------------------------- 1 | 收到一个朋友的求救,他现在遇到一个难题。 2 | 3 | 就是刚刚入职的公司,项目没有GIT,用的老版本的TP,CI,代码也很凌乱,目录也是非常的多,有的没的都在服务器上放着,服务器上还有将近30G的用户头像存着,总之就是一个字 “乱” 4 | 5 | 我准备对这个乱到无法整理的公司项目、服务器及架构进行重新整理,接下来,请看我的表演~ 6 | 7 | # 备份 8 | 既然没有git,那就不用git了,直接在线上打包,ftp链接上下载,在毫无头绪的情况下,我建议直接重装。。如果你没有十足把握,建议不要送死。 9 | 10 | #### 公司业务总结构 11 | * 新接口应用程序 12 | * 后台应用程序 13 | * 老接口应用程序 14 | 15 | 仔细探查了下,资源文件只有头像存在本地,其他的都存到了七牛上。这样打包就好办了。不会影响到用户的正常使用。 16 | 17 | 打包后将项目下载到本地。 18 | 19 | 数据库用的阿里云的,所以直接忽略。 20 | 21 | > 此过程做了5个小时。。。东西确实不少。 22 | 23 | # 配置 24 | 公司用的是Apache,找到vhost文件,拷贝一份到本地,整理一下所有的配置文件,目录什么的。 25 | 他公司要求使用Nginx,所以我需要一个个整理出来配置文件,随后再找到ssl for nginx的证书,一切准备就绪。 26 | 27 | # 无感知转发 28 | 我自己有一台4核4g的阿里云ECS,他们公司的量也不大,所以我准备先将项目部署到我服务器上,在公司服务器上进行负载均衡,将我方IP权重调高,将用户流量全部引入到我方服务器上。 29 | > Demo 如下: 30 | ``` 31 | ProxyPass / balancer://proxy/ 32 |
2 | # 前言
3 |
4 | 大家好,我是CrazyCodes,相信很多手握PHP大刀的朋友都认识我,在这2019年末,我为大家分享我2019年心酸历程。在2019年里
5 |
6 | - 我从安逸的公司跳槽准备找一家可以燃起斗志的公司
7 | - 最终没找到,最后集结几个小伙伴着手开始创业
8 | - 我与女友结束了长达七年的爱情长跑,最终步入婚姻殿堂
9 | - 现在,我正在为创业寻找出路
10 |
11 | # 聊聊segmentfault
12 | 我算是思否比较早的一批用户,在2015年的时候加入思否的。伴随的思否一起成长,还有几天时间,我就踏踏实实的在圈内混了八年了。我算不上什么大佬,出身野路子,摸爬滚打这些年,算是积累了一些项目经验。分享了一些个人见解。
13 |
14 | 嘚瑟一下这些年在思否的成果吧。
15 | 总共产出了76篇文章,总阅读量为180772,获得4351次各位的认可,总粉丝数8483
16 |
17 | 嘚瑟结束,回到正题
18 |
19 | # 聊聊我的经历
20 | 我本是父母眼里毫无前途的小毛孩,步入开发这个大家庭也是出于兴趣,从小学就开始跑网吧的我,不知道被揍过多少次了,我应该是最早的一波写游戏脚本的人,那时候并不是出于乐趣,单纯是想在游戏里风雨一番(幸好知错就改,早早的抛弃了这门手艺😆)
21 |
22 | 我从小唯一的乐趣就写画画(真人素描),中考那年,我以优质的105分的艺术分加100多分的文化课,成功落榜所有高中,最终只能被父母安排到市里的技校。
23 |
24 | 第一次接触编程语言,是在搬入学校宿舍后捡到的学哥的一本C语言。这是开启的编程之路的开始。因为本身在网吧里有的没的学过一些。便一直深入的学下去了,可惜本身没有基础,上学的时候也没好好学习,最终放弃了,改为做前端(那时候叫网页制作)
25 |
26 | 随后来到北京培训,就这样,一年、二年、三年...一直到现在
27 |
28 | # 聊聊为什么离职
29 | 我认为如果在一家公司工作,如果没有主人翁感,没有编码的热情,仅仅是为了拿每个月的工资,那就趁早离职吧。
30 |
31 | 我是在2016年进入的我上一家公司,这也是我人生的转折点,是这家公司彻底的改变了我。
32 |
33 | 刚开始的每位同事还是热血沸腾的,我们每天加班加点的去完成项目,当时条件还是比较艰苦的,没有空调,夏天热的要死,冬天冻的带手套敲代码,就这样持续了大半年的时间。项目总算上线了。
34 |
35 | ...
36 |
37 | 中间的心酸历程就不说了,到了第三年,也就是今年,我决定离开温室。抛掉所谓的技术总监,舒适的办公环境和友爱的小伙伴们。
38 |
39 | 其实为什么离职了,原因就俩个字:腻了
40 |
41 | 程序员需要什么?成就感与满足感。如果每段代码都敲打的毫无意义,我变认为不需要在待着了。
42 |
43 | # 为什么要创业?
44 | 创业嘛,梦而已~ 尽量让自己的理由随意点(其实不是太想说真实想法)
45 |
46 | 为了实现梦想而创业的人不在少数。成功的人屈指可数,甚至根本就没有
47 |
48 | - 2019年5月31号 公司成立,拉拢小伙伴一起搞
49 | - 2019年8月1号 第一个项目上线
50 | - 2019年9月26号 第一个项目失败
51 | - 2019年10月10号 蜗行项目启动
52 | - 2019年11月25号 蜗行上架AppStore
53 | - 2019年12月27号 我依旧在找出路
54 |
55 | 其实吧,我希望拥有这样刺激的人生,唯唯诺诺的工作一辈子不是我想要的结果,老了连吹牛逼的资本都没有,不是嘛?~
56 |
57 | 这半年的时间,见证了什么是人心,看透了所谓的兄弟情义。
58 | 坚持未必是成功,但是缺成为接近成功的开始,敢于迈出第一步,我认为首先在意义上我是成功的,剩下的...
59 |
60 | 五味杂瓶已打翻,就讲这些吧。
61 |
62 | 创业的苦其实说不出,但说句题外话,切记不要兼职创业,梦本来就很难实现了,但却只留下仅仅几个小时去做,就根本没有可能了。
63 |
64 | 我是CrazyCodes,2020年我在创业的路上
65 |
66 | # 致谢
67 | 感谢你看到这里,希望本篇文章可以让你在创业前在想好
--------------------------------------------------------------------------------
/PHP/PHP程序员必备工具.md:
--------------------------------------------------------------------------------
1 | > 行装无兵器,怎能行走天下呢。
2 |
3 | # Postman
4 |
5 | 
6 |
7 | Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
8 |
9 | Postman适用于不同的操作系统,Postman Mac、Windows X32、Windows X64、Linux系统,还支持Postman 浏览器扩展程序、Postman chrome应用程序等。
10 | ## 个人想法
11 | 现代因前端不断发展,大部分公司都做到前后端分离,PHP已变成纯纯的后端语言,大部分功能的PHP都在开发接口相关的工作。所以你不可缺少它。
12 | ## 官网
13 | https://www.getpostman.com/
14 |
15 | # PhpStorm
16 | 
17 |
18 | PhpStrom 是一款php程序开发的开发工具,提供windows,linux,mac版本
19 | ## 个人想法
20 | 你无法拒绝的万能编辑器,官网吹牛逼的一句话 Lightning-smart PHP IDE , PhpStrom是收费的,请支持正版
21 | ## 官网
22 | http://www.jetbrains.com/phpstorm/
23 | # Lantern
24 |
25 | 
26 |
27 | 遇到问题请谷歌,勿百度。
28 | 这是一款轻量级的*墙软件
29 | ## 个人想法
30 | 作为一名开发者,还是需要经常的去国外的技术论坛、博客去了解最新技术。光吃人家吃剩下的也不好是吧~
31 | ## 官网
32 | http://www.getlandeng4.org/
33 | # Sequel Pro
34 |
35 | 
36 |
37 | Sequel Pro是一个快速、易用的Mac数据库管理应用程序,用于使用MySQL数据库。完美的Web开发伙伴,无论您是……
38 |
39 | ## 个人想法
40 | 如果你使用的是mac,我建议你用Sequel Pro,这是我在mac上使用bug最少的数据库管理软件了
41 | ## 官网
42 | http://www.sequelpro.com/
43 | # VLC & OBS
44 | VLC 和 OBS 都是屏幕录制和视频直播软件,如果你在开发直播相关业务时,他俩是好帮手
45 | # Charles
46 |
47 | 
48 |
49 | Charles是一个HTTP代理/ HTTP监视器/反向代理,使开发人员能够查看他们的机器和Internet之间的所有HTTP和SSL / HTTPS通信。这包括请求、响应和HTTP头(其中包含cookie和缓存信息)。
50 | ## 个人想法
51 | 不仅仅是看自己的,也可以看别人的。一般我用到这个都是在摸索大厂内接口参数及开发的规范。
52 | 这可是个神奇的工具,请自行下载体验
53 | ## 官网
54 | https://www.charlesproxy.com/
55 | # iTerm
56 | 
57 |
58 | ## 个人想法
59 | iTerm是mac上自我感觉最完美的终端工具了,功能强大且不失优雅
60 |
61 | ## 官网
62 | https://www.iterm2.com/
63 | # PsySH
64 | 一个可提示开发及学习效率的工具。这里不过多解释,自行下载去享受你的开发过程
65 | https://psysh.org/
66 |
67 | [1]: https://segmentfault.com/img/bVbcN63
68 | [2]: https://segmentfault.com/img/bVbcN7p
--------------------------------------------------------------------------------
/PHP/PHP程序员必看书籍.md:
--------------------------------------------------------------------------------
1 | > 以下都是我看过都书籍,无论是新手或中级别的工程师,我可以保证内容质量,大佬可跳过
2 | # 概述
3 | 知识无价,还是建议各位童鞋把更多的资金投入到学习中。书名排名不分前后
4 | # 《PHP7内核剖析》
5 | 
6 |
7 | 深入理解PHP最佳良品
8 |
9 | https://item.jd.com/20376348917.html
10 |
11 | # 《鸟哥的Linux私房菜》
12 | 
13 |
14 | PHP码农最爱的LINUX入门书籍
15 | https://item.jd.com/1551951406.html
16 |
17 | # 《Modern PHP (中文版)》
18 | 
19 |
20 | 学习(新)PHP的好书
21 | https://item.jd.com/10006210180.html
22 | # 《Learning PHP设计模式》
23 | 
24 |
25 | 不会设计模式,你跟我说你是程序员?
26 | https://item.jd.com/11421261.html
27 | # 《细说PHP》
28 | 
29 |
30 | 这是我入门PHP的时候看的一本书,内容虽然有些摘抄,但知识点概括的很全
31 | https://item.jd.com/1153543995.html
32 | # 《高性能PHP 7》
33 |
34 | 
35 |
36 | PHP全球开发者大会上推荐的一本书,感觉内容很前卫、新鲜。吕毅大佬翻译的
37 | https://item.jd.com/12134218.html
38 | # 《Docker技术入门与实战》
39 | 
40 |
41 | 你确定你不了解下Docker ?
42 | https://item.jd.com/12121728.html
43 | # 《疯狂JAVA讲义》
44 | 
45 |
46 | 其实Java应该在PHP开发者的必会名单内
47 | https://item.jd.com/12261787.html
48 | # 《Redis入门指南(第2版)》
49 | 
50 |
51 | nosql 了解一下?
52 | https://item.jd.com/11685574.html
53 |
54 | # 《算法谜题》
55 | 
56 |
57 | 代码敲累了?来~换换脑子
58 | https://item.jd.com/11412901.html
59 |
60 | # 致谢
61 | 感谢各位看完本篇白话文,最近几天一直在写相当于过往经历一样的文章,并不是不去创造新的内容,而是我更希望把我所知道的,我做过的,告诉大伙,好的地方可借鉴,差的地方可预防。我也不是一个大佬。互相学习把。谢谢
--------------------------------------------------------------------------------
/Shop/电商系统设计之商品[番外篇].md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # 前言
5 | 这是电商系统设计系列在商品设计这块的最后一篇文章。以下是其他文章地址,按照逻辑顺序排列如下
6 | - 电商系统设计之用户系统 https://blog.fastrun.cn/2018/06/14/1-10/
7 | - 电商系统设计之购物车 https://blog.fastrun.cn/2018/06/19/1-12/
8 | - 电商系统设计之商品 (上) https://blog.fastrun.cn/2018/07/08/1-26/
9 | - 电商系统设计之商品 (中) https://blog.fastrun.cn/2018/07/11/1-28/
10 | - 电商系统设计之商品 (下) https://blog.fastrun.cn/2018/07/16/1-29/
11 | - 电商系统设计之订单 https://blog.fastrun.cn/2018/07/27/1/
12 | - 电商系统设计之商品接口 https://blog.fastrun.cn/2018/08/03/1-36/
13 |
14 | 在以上文章中,有些地方描述的不够全面,这篇文章就当补个漏了。
15 |
16 | # 运费模版
17 | 运费模版的设计一般依照淘宝的设计来就比较完美了。
18 |
19 | | 配置 | 说明 |
20 | | -- | -- |
21 | | 模版名称 | 用于商家选择模版 |
22 | | 所在地区/详细地址 | 主要是标记货源在什么位置 |
23 | | 发货时间 | 承诺多长时间可以发货 |
24 | | 是否包邮 | 很多买家都比较关心这个|
25 | | 运费 | 如果不包邮,运费是多少|
26 | | 件数/重量/体积 | 这里与运费相关,具体可看下方配图 |
27 | | 指定条件包邮| 某些地区包邮,例如本市或者相邻的市等等 |
28 |
29 | 
30 |
31 | # 商家相关
32 | | 配置 | 说明 |
33 | | -- | -- |
34 | | 商家名称 | 显示在商品详情页等等 |
35 | | 商家地址 | 用于匹配用户所在地区|
36 | | 详细地址 | 用于记录 |
37 | | 所属行业 | 用于匹配 |
38 |
39 | 如果是一家创业电商公司并且是一个多商户的电商,那你至少需要收集以下商户的相关信息
40 |
41 | - 法人代表姓名
42 | - 联系方式
43 | - 代理商公司
44 | - 代理商公司地址
45 | - 代理商公司法人
46 | - 公司邮箱
47 | - 法人身份证正反面
48 | - 营业执照
49 | - 开户行相关信息
50 | - 其他资质(授权书,资格证明等等)
51 |
52 | # 商品配置
53 | | 配置 | 说明 |
54 | | -- | -- |
55 | | 收藏数 | 商品收藏数只需记录用户点击收藏即可,取消收藏收藏数其实并不需要减少 (自行理解,不能多说)|
56 | | 分享数 | 每次分享都需要记录 |
57 | | 上架设置 | 立即上架、定时上架还是不上架 |
58 | | 是否推荐 | 推荐到指定搜索关键字或者是首页类目等等|
59 |
60 | # 其他配置
61 | | 配置 | 说明 |
62 | | -- | -- |
63 | | 商品关键字(商品属性) | 手动填写,方便检索 |
64 | | 虚拟销量 | 前期总得有点假数据的嘛 |
65 | | 是否允许退换货 | 有些商品是可能无法退换货的,类似与互联网文档、源码等 |
66 | | 减库存方式(拍下减、付款减、不减) | 有些商品是无库存限制的,类似于互联网文档、源码等 |
67 | | 购买权限(单次最低购买数、一定时间段内最多购买数)| 有些商品是限购的或者某段时间只能购买指定数量 |
68 | | 是否包邮、包邮条件 | 这项并非单独的配置,一般在运费模版内设置 |
69 | | 商品付款通知 | 商品购买是否通知,如何通知,通知到哪里 |
70 | | 库存提醒 | 库存降低到指定数量提交商家补货 |
71 | | 自动下架 | 库存降低到指定数量自动下架 |
72 |
73 |
74 | # 致谢
75 | 感谢你看到这里。
76 |
77 | 电商系统商品相关的文章已经到了尾声,如果有其他商品相关的文章需要编写,可以私信联系我,毕竟我也是公司员工,写这些文章并不是我的工作,只是记录我的职业生涯。当然我也希望可以帮助到各位。例如优惠券、活动(限时优惠)等等将在其他章节为大家呈现,谢谢。
78 |
79 | **代码多变,初心不变**
--------------------------------------------------------------------------------
/Nginx/NGINX日志配置总结.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 本来准备讲解nginx和apache的日志的,但是个人不太推荐apache(纯属个人爱好),这里就不介绍apache的日志了。
5 |
6 | 作为一名程序员,比码代码还重要那么一点点的东西就是日志的分析和查询。下面列出常见日志及设置方法。
7 |
8 | # 配置文件
9 |
10 | > nginx分access_log和error_log两种日志
11 |
12 | 设置需要在nginx.conf中,默认通过源码包编译安装nginx目录应在
13 | ```
14 | /usr/local/nginx
15 | ```
16 | 目录下,如果你通过yum或者其他方式安装,不清楚或不知道nginx具体安装目录,可以使用
17 | ```
18 | find / -name nginx.conf
19 | ```
20 | or
21 | ```
22 | nginx -V | grep prefix
23 | -------------
24 | nginx version: nginx/1.13.9
25 | built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
26 | built with OpenSSL 1.0.2k-fips 26 Jan 2017
27 | TLS SNI support enabled
28 | configure arguments: --prefix=/usr/local/nginx --with-http_ssl_module
29 | ```
30 | # 开启访问日志
31 | 如果是你源码包默认安装的,打开路径如下
32 | ```
33 | vim /usr/local/nginx/nginx.conf
34 | ```
35 | 找到如下内容
36 | ```
37 | http {
38 | include mime.types;
39 | default_type application/octet-stream;
40 |
41 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
42 | '$status $body_bytes_sent "$http_referer" '
43 | '"$http_user_agent" "$http_x_forwarded_for"';
44 |
45 | access_log logs/access.log main;
46 |
47 | ...
48 | }
49 | ```
50 | 将log_format到access_log的注释打开即可,log_format可定义nginx的日志规格。
51 |
52 | ## log_format默认规格参数表
53 | | 名称 | 注解 |
54 | | -- | -- |
55 | |$remote_addr | 客户端/用户的IP地址|
56 | | $time_local | 访问时间 |
57 | | $request|请求方式 + 请求地址 |
58 | | $status | 请求状态码 与HTTP状态码一致 |
59 | | $body_bytes_sent | 请求的地址大小 以bytes格式计算|
60 | | $http_referer | 请求来源,从什么地方访问的 |
61 | | $http_user_agent | 用户信息(浏览器信息) |
62 | | $http_x_forwarded_for |转发IP地址 |
63 |
64 | # 开启错误日志
65 | 如果是你源码包默认安装的,打开路径如下
66 | ```
67 | vim /usr/local/nginx/nginx.conf
68 | ```
69 | 找到如下内容
70 | ```
71 | error_log logs/error.log;
72 | #error_log logs/error.log notice;
73 | #error_log logs/error.log info;
74 | ```
75 | 将注解删除即可,你可以将不同的错误类型分开存储如
76 | ```
77 | error_log logs/error.log notice;
78 | ```
79 | notice既为错误类型,不写则是全部。
80 |
81 | # 致谢
82 | 感谢你看到这里,日志操作与分析的相关文章后面我还会写一些,希望可以帮助到你。谢谢
83 |
84 | ** 代码多变,初心不变 **
85 |
--------------------------------------------------------------------------------
/Shop/[还魂篇] 初来乍到如何致人于死地.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | > 逗乐篇,本章的代码无论大牛还是菜🐔都写过
4 | > 统计一些狗血的代码
5 |
6 | # 前言
7 | 初来乍到如何致人于死地,这个标题起的有点血腥恐怖了,代码写不好,吃饭吃不好。本章我们一起来回忆下那些年的“烂代码”,整理不全请见谅,以下都是我写过的😄
8 |
9 | # 箭头
10 | 望京soho,前方左拐、右拐然后右拐
11 | ```
12 | if(){
13 | if(){
14 | if(){
15 | if(){
16 | if(){
17 |
18 | }
19 | }
20 | }
21 | }
22 | }
23 | ```
24 | # 面条
25 | 来碗拉面
26 | ```
27 | if(){
28 | $data = [];
29 | if(){
30 | foreach ($variable as $key => $value) {
31 | # code...
32 | }
33 | if(){
34 | if(){
35 | for ($i=0; $i < ; $i++) {
36 | # code...
37 | }
38 | if(){
39 | foreach ($variable as $key => $value) {
40 | # code...
41 | }
42 | }
43 | }
44 | }
45 | foreach ($variable as $key => $value) {
46 | # code...
47 | }
48 | }
49 | }
50 | ```
51 | # 乱炖
52 | 精通各种写法的你,请低调
53 | ```
54 | $UserName = $_POST['user_name'];
55 | $passWord = $_POST['Password'];
56 |
57 | if($passWord == DB->pass_word){
58 | echo '...'
59 | }
60 | ```
61 | # 嵌套
62 | 少用一个是一个的
63 | ```
64 | foreach ($variable as $key => $value) {
65 | # code...
66 | foreach ($variable as $key => $value) {
67 | # code...
68 | }
69 | }
70 | ```
71 |
72 | # 提前
73 | 事要提前做,能用sql用sql
74 | ```
75 | $result = $this->db->get(self::$newModel)->row_array();
76 | if ($result) {
77 | $result['img'] = $this->getImg($result['NewsMatter']);
78 |
79 | $result['state'] = 3;
80 |
81 | $result['type'] = 0;
82 |
83 | $result['click'] = strlen($result['click']);
84 |
85 | unset($result['NewsMatter']);
86 | }
87 | ```
88 |
89 | # 同姓
90 | 切勿改名换姓
91 |
92 | 
93 |
94 | # 开发规范
95 | 我在开发中的命名规范如下
96 | - 模型 Member[Model]
97 | - 控制器 MemberController
98 | - 公共文件 Common
99 | - 模版(html) member
100 | - JS及其他资源文件 member.js logo.png
101 | - 数据库表名 member member_data member_address
102 | - 字段名 tel sex city_name
103 |
104 | # 致谢
105 | 感谢你看到这里,希望这篇文章能帮助到你和你身边的程序员。有什么问题可在评论区讨论。谢谢
106 |
107 |
108 | [1]: /img/bVbcl2A
--------------------------------------------------------------------------------
/MySQL/MySQL SQL模式特点汇总.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | MySQL服务器可以在不同的SQL模式下运行,并且可以针对不同的客户端以不同的方式应用这些模式,具体取决于sql_mode系统变量的值。DBA可以设置全局SQL模式以匹配站点服务器操作要求,并且每个应用程序可以将其会话SQL模式设置为其自己的要求。
5 |
6 | 模式会影响MySQL支持的SQL语法以及它执行的数据验证检查。这使得在不同环境中使用MySQL以及将MySQL与其他数据库服务器一起使用变得更加容易。
7 |
8 | # 设置SQL模式
9 | 要在运行时更改SQL模式,请sql_mode使用以下SET 语句设置全局或会话 系统变量
10 | ```
11 | SET GLOBAL sql_mode = 'modes';
12 | SET SESSION sql_mode = 'modes';
13 | ```
14 |
15 | # 模式列表
16 | | 模式 | 注释 |
17 | | -- | -- |
18 | | ALLOW_INVALID_DATES | 无效日期会生成错误 |
19 | | ERROR_FOR_DIVISION_BY_ZERO | 除0错误 |
20 | | NO_BACKSLASH_ESCAPES | 禁止使用反斜杠字符(\\)作为字符串中的转义字符。启用此模式后,反斜杠就像其他任何一个普通字符一样。|
21 | | NO_UNSIGNED_SUBTRACTION | 在整数值之间减去(其中一个是类型) UNSIGNED,默认情况下会产生无符号结果。如果结果否则为负,则会导致错误 |
22 | | NO_ZERO_IN_DATE | '0000-00-00' 则允许并且插入产生警告|
23 | | ONLY_FULL_GROUP_BY | select 内指定字段必须出现在 groupby 中,否则错误 |
24 | | STRICT_TRANS_TABLES | 为事务存储引擎启用严格的SQL模式,并在可能的情况下为非事务性存储引擎启用。 |
25 | | STRICT_ALL_TABLES | 为所有存储引擎启用严格SQL模式。无效的数据值被拒绝。 |
26 |
27 | 详情请参考 https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-important
28 |
29 |
30 | # 严格SQL模式
31 |
32 | > MySQL服务器可以在不同的SQL模式下运行,并且可以针对不同的客户端以不同的方式应用这些模式,具体取决于sql_mode系统变量的值。在严格SQL模式下,服务器会将某些警告升级为错误。
33 |
34 | 严格SQL模式适用于以下语句
35 | - ALTER TABLE
36 | - CREATE TABLE
37 | - CREATE TABLE ... SELECT
38 | - DELETE
39 | - INSERT
40 | - LOAD DATA
41 | - LOAD XML
42 | - SELECT SLEEP()
43 | - UPDATE
44 |
45 | > 在存储的程序中,如果在严格模式生效时定义了程序,则列出的类型的单个语句将以严格的SQL模式执行。
46 |
47 |
48 | > 严格的SQL模式适用于以下错误,表示输入值无效或缺失的一类错误。如果值具有错误的列数据类型或可能超出范围,则该值无效。如果要插入的新行不包含其定义中NOT NULL没有显式DEFAULT子句的列的值,则缺少值。
49 |
50 |
51 | - ER_BAD_NULL_ERROR
52 | - ER_CUT_VALUE_GROUP_CONCAT
53 | - ER_DATA_TOO_LONG
54 | - ER_DATETIME_FUNCTION_OVERFLOW
55 | - ER_DIVISION_BY_ZERO
56 | - ER_INVALID_ARGUMENT_FOR_LOGARITHM
57 | - ER_NO_DEFAULT_FOR_FIELD
58 | - ER_NO_DEFAULT_FOR_VIEW_FIELD
59 | - ER_TOO_LONG_KEY
60 | - ER_TRUNCATED_WRONG_VALUE
61 | - ER_TRUNCATED_WRONG_VALUE_FOR_FIELD
62 | - ER_WARN_DATA_OUT_OF_RANGE
63 | - ER_WARN_NULL_TO_NOTNULL
64 | - ER_WARN_TOO_FEW_RECORDS
65 | - ER_WRONG_ARGUMENTS
66 | - ER_WRONG_VALUE_FOR_TYPE
67 | - WARN_DATA_TRUNCATED
68 |
69 |
70 |
71 |
72 | # 致谢
73 | 感谢你看到这里,希望本篇文章可以帮到你,谢谢。🙏
--------------------------------------------------------------------------------
/浅谈重构造成的灾难性毁灭.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 这章我在7月20号的时候就准备好了标题,在那之前有写过一篇重构的文章,这段时间一直在等重构造成的弊端。
5 |
6 | 庆幸的是至今也没挂掉。本章我们来聊聊重构造成的灾难性毁灭。
7 |
8 | # 青铜
9 | 只要你确定你是一个真正的程序员,那当你接手一个新项目时,因为每个人的编码规范与风格不同,或者某块代码出现了问题,作为一名向上的程序员,总会想去重构这个项目更严重的都想重写一遍。例如下面的这类代码
10 | ```
11 | $status = $_POST["status"]
12 |
13 | switch status {
14 | case
15 | ...
16 | break;
17 | case
18 | ...
19 | break;
20 | default:
21 | if(){
22 | ...
23 | }else if(){
24 | ...
25 | }else(){
26 | ...
27 | }
28 | break;
29 | }
30 | ```
31 | 我知道当你看到这段代码内心是崩溃的,如果是名新人,在没有完全理解其结构作用的情况下,绝对不敢擅自动原有的代码,除非他想加班,那怎么办呢?只好在原有的代码上继续增加代码了。
32 | ```
33 | $status = $_POST["status"]
34 |
35 | switch status {
36 | case
37 | ...
38 | break;
39 | case
40 | ...
41 | break;
42 | default:
43 | if(){
44 | ...
45 | }else if(){
46 | ...
47 | }else if(){
48 | ...
49 | }else (){
50 | // 新人写的
51 | }
52 | break;
53 | }
54 | ```
55 | 这块聊的新人入职,一般是不敢动原有代码的。当然这不排除有胆量并且重构的也不错的新人。
56 |
57 | # 白银
58 | 上面聊的与重构并无太大关系,但是必须存在的一段,用于表现程序员重构道路上的勇气。当你到白银差不多需要1-2年的时间,具体时间要个人处在的环境和自学能力。新人不敢动是因为他对语言的基础用法,类库,设计模式都没有特别了解,不敢擅自做动作也是比较聪明、保守的做法。
59 |
60 | 但到了白银就不一样了,我总结了程序员从入门到中级的心理变化,为什么只总结到中级?(PS:作者我自己都没有到高级)这是一个从谦虚到高傲在到什么都不会的过程。
61 |
62 | 
63 |
64 |
65 | 看到这里即可明白重构造成的灾难性毁灭是在2-4年的时期发生的,那个阶段在技术不够扎实但还有一股子改变世界的劲头发生的问题。
66 |
67 | 例如积分系统(这里指新接手的项目),领导让你修改签到获得积分,如果在未全面了解代码结构与功能分布时,擅自修改那必然出现一场不可避免的灾难。一个简单的签到功能的模块复杂度不亚于一个普普通通的企业站。大致有如下模块组成
68 | ```
69 | 用户模块 -> 积分模块 -> 交易模块 -> 明细模块
70 | ```
71 |
72 | 为什么说是必然?除非你在原有基础上改,那这样你又变回了青铜并且也不想那么做。在基础上重构可能当时不会出现问题。不断的有其他接手早晚回出问题。重构的灾难并非指的是一个人或某个人造成的,就如一个水杯,每个人看到都倒入一滴水,当溢出来时就发生了所谓的“灾难”
73 |
74 | # 黄金
75 | 到这个段位后,很多人都变得聪明了,不在基础代码上修改,更不去所谓的重写。单独拿出一个文件写功能不就好了。事情还远没有那么简单。
76 |
77 | 
78 |
79 | 就如上图那二货,认为自己可以,但最终Over。在项目开发上我们见过很多类型的情况。重构的方式方法有很多种,因人因物(项目)而异,对项目作出合理的分析后再对其作出一部分细节的重构,日月累计最终成形。
80 |
81 | # 总结
82 | 如果你正在做重构应考虑以下几点
83 | - 成本
84 | - 工期
85 | - 代码的优雅与简洁
86 | - 可扩展性等等
87 | 为什么要重构?原有代码无法更好的扩展,代码可读性差无法在其基础上修改的等等原因。希望会对你提供重构质量有一定帮助。
88 |
89 | # 致谢
90 | 感谢你看到这篇文章,希望可以帮到你,谢谢。
--------------------------------------------------------------------------------
/MySQL/MySQL常用函数汇总.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 概述
4 | MySQL常用函数汇总,希望可以帮到你,没事看看当复习
5 | # 字符串函数
6 | | 函数 | 功能 |
7 | | -- | -- |
8 | | CONCAT(s1,s2,....) | 字符串连接 |
9 | | INSERT(str,x,y,instr) | 将指定开始标记到结束的字符串替换为指定字符串 |
10 | | LOWER(str) | 将字符串所有字符转为小写|
11 | | UPPER(str) | 将字符串所有字符串转为大写|
12 | | LEFT(str,x) | 返回字符串str最左边的x个字符|
13 | | RIGHT(str,x) | 返回字符串str最右边的x个字符 |
14 | | LPAD(str,n,pad)| 在str最左边填充n个pad|
15 | | RPAD(str,n,pad)| 在str最右边填充n个pad|
16 | | LTRIM(str) | 去掉字符串str左侧的空格|
17 | | RTRIM(str) | 去掉字符串str右侧的空格|
18 | | REPEAT(str,x)| 返回str重复x次的结果|
19 | | STRCMP(s1,s2)| 比较字符串s1和s2|
20 | | REPLACE(str,a,b)|用字符串b替换字符串str中所有出现的字符串a|
21 | | TRIM(str) |去掉字符串行尾和行头的空格 |
22 | | SUBSTRING(str,x,y) | 返回从字符串str x位置起y个字符长度的字串|
23 | # 数学函数
24 | | 函数 | 功能 |
25 | | -- | -- |
26 | | ABS(x) | 返回x的绝对值|
27 | | CEIL(x) | 返回大于x的最小整数值 |
28 | | FLOOR(x) | 返回小于x的最大整数值|
29 | | MOD(x,y) | 返回x/y的模|
30 | | RAND()| 返回 0~1内的随机值|
31 | | ROUND(x,y) | 返回参数x的四舍五入的有y位小数的值|
32 | | TRUNCATE(x,y)| 返回数字x截断位y位小数的结果|
33 | # 日期和时间函数
34 | | 函数|功能|
35 | | -- | -- |
36 | | CURDATE() | 返回当前日期|
37 | | CURTIME() | 返回当前时间|
38 | | NOW() | 返回当前的日期和时间|
39 | | UNIX_TIMESTAMP(date) | 返回日期date的UNIX时间戳|
40 | | FROM_UNIXTIME| 返回UNIX时间戳的日期值|
41 | | WEEK(date) | 返回日期date为一年中的第几周 |
42 | | YEAR(date) | 返回日期date的年份|
43 | | HOUR(time) | 返回time的小时值|
44 | | MINUTE(time) | 返回time的分钟值|
45 | | MONTHNAME(date) | 返回date的月份名|
46 | | DATE_FORMAT(date,fmt) | 返回按字符串fmt格式日期date值|
47 | | DATE_ADD(date,interval expr type)| 返回一个日期或时间值加上一个时间间隔的时间值|
48 | | DATEDIFF(expr,expr2) | 返回起始时间expr和结束时间expr2之间的天数|
49 | # 流程函数
50 | | 函数 | 功能|
51 | | -- | -- |
52 | | IF(value,t f) | 如果value是真,返回t;否则返回f|
53 | | IFNULL(value1,value2)| 如果value1不为空,返回value1,否则返回value2|
54 | | CASE WHEN [value1] THEN[result1]...ELSE[default]END|如果value1是真,返回result1,否则返回result|
55 | | CASE[expr] WHEN [value1]THEN[result1]...ELSE[default]END|如果expr等于value1,返回result1,否则返回default|
56 | # 其他常用函数
57 | | 函数| 功能|
58 | | -- | --|
59 | | DATEBASE() | 返回当前数据库名|
60 | | VERSION()| 返回当前数据库版本|
61 | | USER() | 返回当前登录用户名|
62 | | INET_ATON(ip) | 返回ip地址的数字表示|
63 | | INET_NTOA(num) | 返回数字代表的ip地址|
64 | | PASSWORD(str) | 返回字符串str的加密版本|
65 | | MD5() | 返回字符串str的md5值|
66 |
67 | # 其他文章
68 | 我整理的另一篇文章
69 | MySQL常用系统表汇总 : https://blog.fastrun.cn/2016/07/10/1-27/
70 |
71 | # 致谢
72 | 感谢你看完这篇文章,我相信初中级码农每天都在接触SQL,熟记mysql函数就跟熟记php函数一样重要。谢谢🙏
73 |
74 |
75 | [1]: /img/bVbdCe2
--------------------------------------------------------------------------------
/Nginx/安装Nginx要从娃娃抓起.md:
--------------------------------------------------------------------------------
1 | 本文翻译与2017年8月26日
2 |
3 | 使用configure命令配置构建。它定义了系统的各个方面,包括允许使用nginx进行连接处理的方法。最后它创建一个Makefile。该configure命令支持以下参数:
4 |
5 | - **--prefix=path** 定义一个nginx存储的系统目录,这个目录将存储nginx的代码和配置文件。如果不设置将默认安装到 /usr/local/nginx 目录中。
6 |
7 | - **--sbin-path=path** 设置一个nginx可执行文件的路径。默认情况下路径为 prefix/sbin/nginx
8 |
9 | - **--conf-path=path** 设置默认nginx配置文件的所在目录,你可以通过不同的配置文件启动nginx,在启动的时候加上参数 -c fileprefix/conf/nginx.conf 即可
10 |
11 | - **--pid-path=path** 设置将存储主进程ID的nginx.pid文件的路径。默认情况下文件被安装到 prefix/logs/nginx.pid。
12 |
13 | - **--error-log-path=path** 设置错误、警告、提醒等错误信息的存放文件及目录,默认会安装到
14 | prefix/logs/error.log 内。安装后可以在nginx.conf配置文件中修改error_log选项来更改。
15 |
16 | - **--http-log-path=path** 设置服务器请求日志文件的名称。安装后可通过nginx.conf内access_log选项修改,默认命名为 prefix/logs/access.log
17 |
18 | - **--build=name** 设置一个可选的nginx构建名称。
19 |
20 | - **--user=name** 设置nginx的所属用户,正常情况下我都会建立一个nginx的用户用于指定,默认的用户是nobody。
21 |
22 | - **--group=name** 设置nginx的所属群组,正常情况下我都会建立一个nginx的群组用于指定,默认为nobody组
23 |
24 | - **--without-http_gzip_module** 不编译压缩的HTTP服务器的响应模块。编译并运行此模块需要zlib库。
25 |
26 | - **--without-http_rewrite_module** 不编译重写模块。编译并运行此模块需要PCRE库支持。
27 |
28 | - **--without-http_proxy_module** 禁用构建HTTP服务器代理模块。
29 |
30 | - **--with-http_ssl_module** 编译https模块,让服务器可以支持https协议。
31 |
32 | - **--with-pcre=path** 指定PCRE的路径
33 |
34 | - **--with-pcre-jit** 使用“即时编译”支持构建PCRE库(1.1.12, pcre_jit指令)。
35 |
36 | - **--with-zlib=path** 指定zlib的路径
37 |
38 |
39 | ----------
40 | 下面的这几项配置我也不咋懂,无法合理的去解释,了解的朋友可以私信我
41 |
42 | - **--with-select_module** --without-select_module 启用或禁用构建允许服务器使用该select()方法的模块 。如果平台似乎不支持更合适的方法(如kqueue,epoll或/ dev / poll),则会自动构建该模块。
43 |
44 | - **--with-poll_module** --without-poll_module 启用或禁用构建允许服务器使用该poll()方法的模块 。如果平台似乎不支持更合适的方法(如kqueue,epoll或/ dev / poll),则会自动构建该模块。
45 |
46 | - **--with-cc-opt=parameters** 设置将添加到CFLAGS变量的其他参数。当在FreeBSD下使用系统PCRE库时, --with-cc-opt="-I /usr/local/include" 应该指定。如果支持的文件数量select()需要增加,也可以在这里指定,如: --with-cc-opt="-D FD_SETSIZE=2048"。
47 |
48 | - **--with-ld-opt=parameters** 设置链接期间将使用的其他参数。当在FreeBSD下使用系统PCRE库时, --with-ld-opt="-L /usr/local/lib" 应该指定。
49 |
50 | 参数使用示例(所有这些都需要输入一行):
51 |
52 | ```
53 | ./configure
54 | --sbin-path = /usr/local/nginx/nginx
55 | --conf-path = /usr/local/nginx/nginx.conf
56 | --pid-path = /usr/local/nginx/nginx.pid
57 | --with-http_ssl_module
58 | --with-pcre = ../pcre-8.41
59 | --with-zlib = ../zlib-1.2.11
60 | ```
61 |
62 | 配置完成后,使用make进行编译和安装。
--------------------------------------------------------------------------------
/PHP/PHP程序员必须知道的两种日志.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 作为一名程序员,比码代码还重要那么一点点的东西就是日志的分析和查询。下面列出常见日志及设置方法。
5 |
6 | # php-fpm 慢日志
7 | php慢日志需要在php-fpm.conf设置,如果使用源码包安装默认请执行下面命令
8 | ```
9 | cp php-fpm.conf.default php-fpm.conf
10 | ```
11 | 默认通过源码包编译安装php目录应在
12 | ```
13 | /usr/local/php
14 | ```
15 | 目录下,如果你通过yum或者其他方式安装,不清楚或不知道php具体安装目录,可以使用
16 | ```
17 | find / -name php-fpm.conf
18 | ```
19 | or
20 | ```
21 | php -i | grep Path
22 | ------------------------------------------
23 | [root@xxxx etc]# php -i | grep Path
24 | Configuration File (php.ini) Path => /usr/local/php/etc
25 | XPath Support => enabled
26 | Path to sendmail => /usr/sbin/sendmail -t -i
27 | [root@xxxx etc]#
28 | ```
29 | ## 开启慢查询日志
30 | 旧的版本是在php-fpm.conf设置 (实际是我忘记了哪个版本),php7.x版本源码包编译后需要www.conf修改慢查询配置
31 | ```
32 | vim /usr/local/php/etc/php-fpm.d/www.conf
33 | ```
34 | 不过配置项都一样的,如果你在php-fpm.conf找不到,就去他的同级目录php-fpm.d下面找下吧。
35 | ```
36 | ; The log file for slow requests
37 | ; Default Value: not set
38 | ; Note: slowlog is mandatory if request_slowlog_timeout is set
39 | ;slowlog = log/$pool.log.slow
40 |
41 | ; The timeout for serving a single request after which a PHP backtrace will be
42 | ; dumped to the 'slowlog' file. A value of '0s' means 'off'.
43 | ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
44 | ; Default Value: 0
45 | ;request_slowlog_timeout = 0
46 |
47 | ```
48 | - slowlog 设置慢查询日志的生成目录
49 | - request_slowlog_timeout 设置慢查询的标准时间(打开此配置就相当于开启了慢查询日志),配置以秒为单位,一般设置3s。
50 |
51 | # php-error 错误日志
52 | 在生产环境中是不允许php报错的,就算报错也是白屏或者500,所以在生产环境中的日志收集是非常重要的。
53 | ## 开启错误日志
54 | 一般情况下,php错误日志的配置都在php.ini文件中
55 | ```
56 | /usr/local/php/etc/php.ini
57 | ---------------------------
58 | error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
59 | display_errors = Off
60 | log_errors = On
61 | ; Log errors to specified file. PHP's default behavior is to leave this value
62 | ; empty.
63 | ; http://php.net/error-log
64 | ; Example:
65 | ;error_log = php_errors.log
66 | ; Log errors to syslog (Event Log on Windows).
67 | ;error_log = syslog
68 | ```
69 | - error_log 错误日志的生成目录
70 | - error_reporting 生产环境错误级别应全开
71 | - display_errors 在页面上不显示错误
72 | - log_errors 开启错误日志
73 |
74 | 最终的结果是
75 |
76 | ```
77 | error_log = /var/log/php_error.log
78 | display_errors = Off
79 | error_reporting = E_ALL
80 | log_errors = On
81 | ```
82 | # 致谢
83 | 感谢你看到这里,日志操作与分析的相关文章后面我还会写一些,希望可以帮助到你。谢谢
84 |
85 | **代码多变,初心不变**
86 |
--------------------------------------------------------------------------------
/是时候了解下Travis CI是什么了.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 首先祝各位朋友新年快乐,工作顺利,事业有成,永无BUG。
5 |
6 | 有些朋友一直疑惑Travis CI是个什么东西,网络上搜索后得知的答案是自动测试,自动发布。自动xx 这个名词貌似非常流行,这也是DevOps的一部分,什么?DevOps是什么?我们下一章讲讲这个。
7 |
8 | 先来解释下“自动”这个词,自动顾名思义是自动完成一些事情,上述的自动测试,并不是各位认知中的人肉测试,而是通过我们自己的规则去测试,例如跑一遍tests内的所有测试,自动发布也是通过脚本对现有项目发布到生产环境中或预发布环境中。
9 |
10 | 万事开头难,咱们只玩简单的。
11 | # 使用
12 | 想玩起来TravisCi不需要安装任何软件,它的网址是 https://travis-ci.org/ , 你可以选择通过GitHub账号登录他。
13 |
14 |
15 | 
16 |
17 | 随后我们可以建立一个github库,就叫它travis_ci_test吧,测试使用就随意点好了。之后点击项目管理 https://travis-ci.org/account/repositories ,会列出你所有的GitHub库
18 |
19 | 
20 |
21 |
22 | 通过点击单选按钮将库添加到TravisCi内。添加完成后并没有完事,这时候我们就该认真看看自动测试、集成、发布的脚本怎么写了。
23 |
24 | # 配置文件
25 | TravisCi为我们准备了超棒的配置文件,你可以在配置文件内随心所欲,例如打开某个目录,执行某条命令,他与dockerfile文件或者shell脚本很类似。只不过运行的容器在travisCi上,并非你本机
26 |
27 | 开发文档:https://docs.travis-ci.com/user/tutorial/
28 |
29 |
30 | 
31 |
32 | 从简单开始,在根目录建立文件 .travis.yml , 下面是具体的配置项
33 | ```
34 | language: php
35 | php:
36 | - 7.1
37 | before_script:
38 | - composer install
39 | ```
40 | 没错,五行配置就足够了,之后我们回到 https://travis-ci.com/dashboard,点击 trigger a build
41 |
42 |
43 | 
44 |
45 |
46 | 点击当前项目看看详情。 https://travis-ci.org/CrazyCodes/travis_ci_test
47 |
48 |
49 | 
50 |
51 | TravisCi 做了几个简单的事情
52 | 1. 开机
53 | 2. 克隆你的GITHUB项目
54 | 3. composer install
55 | 4. phpunit
56 |
57 | 通过测试了就显示success(大绿色) 失败就error喽。下面来看看这个详情页面上都有什么?
58 |
59 |
60 | 
61 |
62 | 上图大概展示了以下几点
63 | - 分支名称
64 | - 提交版本
65 | - 执行时间
66 | - 提交时间
67 | - 开发语言
68 | - 作者名称
69 | - 等....
70 |
71 | 下面则是跑的命令行了。具体的配置文件还需要自己去研究,希望我这个头开的还不错吧。
72 |
73 | # 感言
74 | 当你理解了Travis CI后会感觉这是一个多么优秀的产品,程序员要写出好代码是要求,而这些产品扩展。作为一名优秀的程序员,你其实可以这样做
75 |
76 | - 写测试用例
77 | - 实现业务
78 | - 提交分支 (TravisCi会自动检测提交并测试)
79 | - 自动部署
80 | - 结束任务
81 |
82 | 看似很复杂,不妨试试?
83 |
84 |
85 | # 致谢
86 | 感谢你看到这里,希望本文可以帮到你。谢谢
--------------------------------------------------------------------------------
/PHP/PHP程序员如何优雅的搬砖.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | > 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes/Blog
5 |
6 | # 前言
7 | Hello , 各位Coder !
8 |
9 | 在这里向各位工程师提前拜年 “新年快乐” , 距离年三十已经没有几天了,可能有些朋友还坚持在一线战斗着,有些已经回到家乡陪伴家人。北京每到这个时候都似一座空城,城与城之间表现的那么凄凉。
10 |
11 | 这是年前的最后一篇文章,本章来聊一聊程序员如何优雅的搬砖
12 |
13 | 搬砖既 “为达到目的,不断重复某项工作的行为,其实与造轮子一样,不谋而合”
14 |
15 | # 基础
16 | 
17 |
18 |
19 | 这里的基础并非单指其技术能力,技术底蕴,更有意体现程序员在初期不断重复的工作而获得的感想与意识。想必大家都是这么过来的,第一年时根据需求不断创新,不断磨练。所有的功能都必须自己写,用其他人的不放心。但自己写的东西经常出问题,无论是思路或者代码都不够精炼。一层一层的技术债在完工后不断的涌现出来。当时你会不会有跑路的想法?
20 |
21 | # 选择
22 |
23 | 
24 |
25 | 在不断的进步中,我们积攒了很多经验,这里指的变是开发经验,并非什么技术经验。开发经验大概意思是在看到某项需求时,可以快速的根据自己的知识与经验的储备选择其开发框架、语言、数据库及流程逻辑等。这里就是在做选择,你会对该需求给出自己的几项方案,而不是现查现写。
26 |
27 | # 开源
28 |
29 | 
30 |
31 | 开源的目的是什么?建立一个更好的技术生态圈,Coder与Coder之间互相帮助,达到更好的效果(并不是结对编程哈),现如今PHP的生态圈非常健康的运转,无论是PHP7的发布或者Composer的诞生,都为贵圈提供了更好的技术与实践的支持。我们应更好的去接触、熟练的去运用各位大神费尽心血为我们准备的全新的PHP
32 |
33 | # 本机
34 |
35 |
36 | 讲过很多初学者在本地开发时,对本地的开发环境毫无关心,随随便便拿一个集成开发工具便搭建了一整套的运行环境,对其本质毫无理解,我想大部分人都有过“全干工程师”的历程,对本地环境毫无在意的程序员,敢说在生产、测试环境中依旧无法出色的表现其技术能力。
37 |
38 | # 建议
39 | 在这里,我给出一些常见业务需求的解决方案 (并非是一些高级的东西)
40 |
41 | ## 后台
42 | 如果是从头做一个后台,然而又不想从0开始搭建后台的逻辑,在这里我强烈推荐laravel-admin,laravel虽然性能方面低于其他框架,但其作为后台的开发框架来说我认为还是第一名的。
43 |
44 | - https://laravel.com/
45 | - https://laravel-admin.org/
46 | - https://laravel-admin.org/docs/zh
47 |
48 | laravel-admin 安装比其他的开源程序要简单的多,这都寄托于能力极佳的composer与laravel
49 | ```
50 | composer require encore/laravel-admin
51 |
52 | ```
53 |
54 | ## 接口
55 | 接口开发着重性能,相应速度,如果依旧喜欢laravel,可以选择lumen
56 |
57 | - https://lumen.laravel.com/
58 |
59 | 或者使用C编写的框架 Phalcon
60 |
61 | - https://phalconphp.com/zh/
62 |
63 | 实在感觉这些框架太过庞大,复杂也可以选择 Slim ,他一定精简到让你飞起
64 |
65 | - http://www.slimframework.com/
66 |
67 | 不想使用框架?但从0写还嫌麻烦,这时你可以考虑 鸟哥的Yaf 或者 韩天峰的 Swoole
68 | - https://www.swoole.co.uk/
69 | - http://php.net/manual/en/book.yaf.php
70 |
71 | ## 其他
72 | 熟练去搜集、查找适合自己业务的包,熟练去运用其优势。让自己不需要再重复造轮子,无止尽的还技术债,这才是2019年程序员应该学习的。
73 |
74 | - https://segmentfault.com/a/1190000016004756
75 | - https://segmentfault.com/a/1190000017782596
76 | - https://packagist.org/
77 | - https://github.com/
78 |
79 | # 致谢
80 | 感谢你看到这里,希望本篇文章可以帮助到你,谢谢。
81 |
--------------------------------------------------------------------------------
/Go/PHP To Go 转型手记 (二).md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 作为一名PHP程序员,我感到荣幸。但在时代不断的变迁中,要具备足够的知识才可生存。
5 |
6 | 那就从Go语言学起把。
7 |
8 | 希望看到本篇文章的你可以对Go有一个基本的认识。本系列文章与我自己学习Go语言的方式去描述。以PHP代码与Go代码的对比加以区分理解。
9 | # 加载
10 | PHP
11 | ```
12 | namespace Action
13 | use Action
14 | ```
15 | Go
16 | ```
17 | package Action
18 | import "action"
19 | ```
20 | # 数组
21 | PHP
22 | ```
23 | // 初始化
24 | $arr = []
25 | $arr = array()
26 | // 初始化赋值
27 | $arr = [1,2,3]
28 | // 多维数组
29 | $arr = [][]
30 | // 获取值
31 | echo $arr[1]
32 | // 获取数组总数
33 | echo length($arr)
34 | // 获取数组区间
35 | $a=array("red","green","blue","yellow","brown");
36 | print_r(array_slice($a,1,2));
37 | // 设置key=>value
38 | $arr = ["username"=>"zhangsan","age"=>13]
39 | // 删除指定下标
40 | unset($arr[0])
41 | ```
42 | Go 数组 & 切片 (切片是数组的一个View,就例如MySQL的视图一样)
43 | ```
44 | // 初始化
45 | var arr [5]int
46 | // 初始化赋值
47 | arr := [5]int{1, 2, 3, 4, 5}
48 | // 无需声明数组个数
49 | arr := [...]int{1, 2, 3, 4, 5, 6, 7}
50 | // 多维数组
51 | var arr [4][5]bool
52 | // 获取值
53 | fmt.Println(arr[1])
54 | // 获取数组总数
55 | fmt.Println(len(arr))
56 | // 获取数组区间 显而易见,Go对数组的操作更便利直观
57 | a := [...]string{"red","green","blue","yellow","brown"}
58 | fmt.Println(a[1:2])
59 | // 设置key=>value 这里需要使用Map
60 | m := map[string]string{
61 | "username": "zhangsan",
62 | "age" : "13"
63 | }
64 | // 删除指定下标 Go没有删除数组下标的系统方法
65 | arr := arr[1:]
66 | // 删除中间位置的下标 可通过合并的方式去除指定下标
67 | arr := append(arr[:3],arr[4:])
68 | ```
69 | # 循环结构
70 | PHP
71 | ```
72 | // 基本结构
73 | for($i=0;$i<10;$i++){
74 | echo $i;
75 | }
76 | // 死循环
77 | for($i=0;$i<10;$i++){
78 | echo $i;
79 | $i--
80 | }
81 | // 获取key,value
82 | foreach($arr as $key=>$value){
83 | echo $key,$value
84 | }
85 | ```
86 | Go
87 | ```
88 | // 基本结构
89 | for i := 0; i < 10; i++ {
90 | fmt.Println(i)
91 | }
92 | // 死循环 可见Go写死循环非常方便
93 | for {
94 | fmt.Println("")
95 | }
96 | // 获取key,value
97 | for k, v := range arr {
98 | fmt.Println(k, v)
99 | }
100 | ```
101 |
102 | # 控制结构
103 | PHP
104 | ```
105 | // if
106 | if(true){
107 |
108 | }
109 | // switch
110 | switch(true){
111 | case true:
112 | echo true;
113 | break;
114 | }
115 | ```
116 | Go
117 | ```
118 | // if
119 | if true {
120 |
121 | }
122 | // switch Go语言的Switch的Case不需要break
123 | switch true {
124 | case true:
125 | fmt.Println(true)
126 | }
127 | ```
128 |
129 | # 类
130 | PHP
131 | ```
132 | // 声明一个类
133 | class City{}
134 | ```
135 | Go
136 | ```
137 | // 声明一个结构体 这里并非混淆公众,是因为Go本身没有类的概念,只是其声明及操作方法与类概念相似
138 | type City struct{}
139 | ```
140 | Go语言的结构体会在下一个章节来做对比
141 | # 致谢
142 | 感谢你看到这里,希望本篇文章可以帮到你。谢谢
--------------------------------------------------------------------------------
/Shop/电商系统设计之购物车.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | > 本章适合初级工程师及中级工程师细看,大佬请随意
4 | # 前言
5 |
6 | - 问 [不存价格字段不行吗?直接查询商品表获取价格]
7 | - 答 [如果价格更新,应提示用户,商品的浮动信息。可以选择直接更新购物车,或者单独建立一个表,来记录更新的价格和信息,类似京东]
8 | - 问 [联表查询可以从商品表中知道商品是否上架]
9 | - 答 [商品不存在了如何联,只会将逻辑整复杂,未来包括降价提醒,无货提醒,下架提醒,购物车该如何查询就成了一个问题]
10 |
2 |
3 | # 前言
4 | > 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes/Blog
5 |
6 | 大家好,我是CrazyCodes,这篇文章的标题有些严肃了,算是非常单纯的教学文,愿可以帮助到有需要的人。
7 |
8 | # GitLab
9 | GitLab与GitHub和码云一样,都是以web形式存在的在线管理Git仓库并且都拥有优雅的可视化操作页面。当然这篇文章咱们不介绍这没用的。
10 |
11 | 因为GitLab/Hub或者码云对免费用户不是够友好,无论是在库的容量或者协作人员都有些许限制,GitHub的提交速度对中国程序员更是慢如蜗牛。
12 |
13 | 网络上关于GitLab安装的文章并不少,因为GitLab在安装过程中会默认再安装一个Nginx,这必然会与已有Nginx冲突,写这篇文章主要的目的还是对已存在Nginx服务的服务器如何配置GitLab做一个指导。
14 |
15 | # 安装
16 | 服务器版本:(阿里云) CentOS Linux release 7.6.1810
17 | GitLab版本:12.4.0-ee
18 | Nginx版本:nginx/1.17.0
19 |
20 | 基本都算是比较新的版本了,废话不多说,我们开始吧!
21 |
22 | ## 第一步:进入GitLab官网
23 | [https://about.gitlab.com/install/](https://about.gitlab.com/install/)
24 |
25 | ## 第二步: 执行一大堆牛逼的命令
26 | ```
27 | sudo yum install -y curl policycoreutils-python openssh-server
28 | sudo systemctl enable sshd
29 | sudo systemctl start sshd
30 | sudo firewall-cmd --permanent --add-service=http
31 | sudo firewall-cmd --permanent --add-service=https
32 | sudo systemctl reload firewalld
33 | ```
34 | ## 第三步:下载GitLab安装文件
35 | ```
36 | curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash
37 | ```
38 |
39 | ## 第四步:安装
40 | ```
41 | sudo EXTERNAL_URL="https://gitlab.example.com" yum install -y gitlab-ee
42 | ```
43 | EXTERNAL\_URL 这个配置就默认好了,毕竟咱们也不用GitLab自带的Nginx
44 |
45 | ## 第五步:完成
46 | 就是这么简单,安装完成了。第一次访问的时候会要求重新设置密码。用户名默认为 root
47 |
48 | # 配置
49 | 安装完成后,需要对现有的GitLab做一些配置更改。
50 |
51 | 官方文档:[https://docs.gitlab.com/12.4/omnibus/settings/nginx.html#using-a-non-bundled-web-server](https://docs.gitlab.com/12.4/omnibus/settings/nginx.html#using-a-non-bundled-web-server)
52 |
53 | ## 禁用NGINX
54 | 在文件
55 | ```
56 | /etc/gitlab/gitlab.rb
57 | ```
58 | 下,将配置设置为false
59 | ```
60 | nginx['enable'] = false
61 | ```
62 |
63 | ## 设置所属用户
64 | 需要将自建Nginx的所属用户加入到配置内
65 | 在文件
66 | ```
67 | /etc/gitlab/gitlab.rb
68 | ```
69 | 下,将配置设置为 www-data
70 | > 对于Debian/Ubuntu,默认用户是`www-data`
71 | > 对于RHEL/CentOS,NGINX用户是`nginx`。
72 |
73 | 当然如果安装nginx未指定所属用户,也有可能会是root。当然可以通过命令
74 | ```
75 | cat /usr/local/nginx/conf/nginx.conf | grep user
76 | ```
77 | 查看所属用户,实际就在配置文件里写着
78 | ```
79 | user root;
80 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
81 | # '"$http_user_agent" "$http_x_forwarded_for"';
82 | ```
83 | 回到正题,修改配置为root(刚刚讲过了,为啥是root)
84 | ```
85 | web_server['external_users'] = ['root']
86 | ```
87 | 这个设置是一个数组,可以添加多个用户。设置好后运行命令
88 | ```
89 | sudo gitlab-ctl reconfigure
90 | ```
91 | 配置已生效
92 |
93 | ## 添加虚拟主机
94 | GitLab已经为开发者准备好相应的vhost文件。可以通过访问下方链接获取
95 | [https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server)
96 | 配置完成后执行
97 | ```
98 | nginx -s reload
99 | ```
100 | 既完成,原理很简单,其他配置我也不是太明白,大概则是使用反向代理转发了一下请求这样子。
101 |
102 | # 问题
103 | 在最新版本中,如果按照上述方式配置,可能会在提交过程中出现422错误,具体原因不清楚,我遇到这个问题后配置了ssl,解决了这个问题。
104 |
105 | # 致谢
106 | 大多都是官方的文字,可能只是不太好找,所以网络上经常出现这个问题,但都无解,所以写这么个水文,帮助需要的人,当然,提倡还是去看官网的文档。
107 |
108 | 感谢你看到这里,希望本篇文章可以帮到你。
109 |
--------------------------------------------------------------------------------
/Supervisor 从入门到放弃.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | > Supervisor是一个客户端/服务器系统,允许其用户在类UNIX操作系统上控制许多进程。(官方解释)
5 |
6 | 简单点来讲,就是一个监控脚本运行的工具,不过他可以统一化管理,laravel的队列文档上也有相关使用方式方法,例如
7 |
8 | - 定时脚本的启动、重启、关闭和日志监控
9 | - swoole的启动、重启、关闭和日志监控 (众所周知,swoole大部分的特性都只能在cli中运行)
10 | - redis的启动、重启、关闭和日志监控 (redis自身未提供类似phpmyadmin的后台可视化工具)
11 | - laravel中的队列、一些自动化的脚本、workman等等的脚本
12 |
13 | 一般都使用 ``` &test.sh ``` 让其来保证在后台运行,但在很多情况下,无法对脚本个人化的监控。这时你可能就需要 Supervisor 来帮助你。你大可把它当作你的unix系统的可视化管理后台。下面来让我们见证它的强大之处。
14 |
15 | # 安装
16 | Supervisor 有多种安装方式,我推荐其中最简单也是最容易安装的一种
17 | ```
18 | apt-get -y install python-setuptools
19 | easy_install supervisor
20 | ```
21 | 正如你所见,两条命令即完成安装
22 | # 配置
23 | Supervisor安装完成后,运行 ```echo_supervisord_conf```。这将打印一个示例的Supervisor配置文件到您的终端。只要你能看到打印的配置文件内容。
24 |
25 | Supervisor 不会自动生成配置文件。
26 |
27 | 请使用命令 ```echo_supervisord_conf > /etc/supervisord.conf ``` 来生成配置文件。
28 |
29 | ## 部分配置文件信息表
30 | | 名称 | 注释 | 栗子 |
31 | | -- | -- | -- |
32 | | inet_http_server[port] | 内置管理后台 |*:8888 |
33 | | inet_http_server[username] | 管理后台用户名 | admin|
34 | | inet_http_server[password] | 管理后台密码 | admin |
35 | | include[files] | 设置进程配置文件格式 | /etc/supervisor/supervisor.d/*.ini |
36 |
37 | # 运行
38 | Supervisor 启动需加载配置文件
39 | ```
40 | supervisord -c /etc/supervisor/supervisord.conf
41 | ```
42 | 停止命令是
43 | ```
44 | supervisorctl shutdown
45 | ```
46 | 重新加载配置文件
47 | ```
48 | supervisorctl reload
49 | ```
50 |
51 | Supervisor 以 ```[program:[your_cli_name]] ``` 以每段进程配置文件的开头,your_cli_name 则是你的进程名称,名称会显示在Supervisor后台管理工具和Supervisor cli命令输出上。我们以运行php-fpm为例
52 | ```
53 | [program:php7]
54 | command=php-fpm
55 | ```
56 | 哦呦,就是酱紫简单。没有过多的废话。或者运行一段shell。
57 | ```
58 | [program:echo]
59 | command=sh echo.sh
60 |
61 | --------------------------------
62 |
63 | echo.sh
64 |
65 | your_name="my name zhangsan"
66 | echo $your_name
67 |
68 | ```
69 | 当然laravel队列也是依旧简单
70 | ```
71 | [program:laravel-worker]
72 | command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3
73 | ```
74 | 当然这里只是简单的演示,让你可以快速上手,配置脚本内不仅仅只有command命令。
75 | 具体可见官方文档 http://www.supervisord.org/configuration.html#program-x-section-settings
76 | # 后台
77 | Supervisor提供的后台管理比较简单
78 |
79 | 
80 |
81 | 大致功能有 重启、启动、停止进程,打印日志,清除日志等。基本上就这么几个简单的功能,当然也没有宕机报警,日志报警什么的。不过强大的Supervisor为我们提供了接口😄
82 |
83 | # 接口
84 | 通过API能获取基本所有的信息,例如进程列表,某个进程的状态,进程日志。包括对进程的重启、停止、开启等操作,将Supervisor彻底集成到内部监控后台也是没有什么问题的。
85 |
86 | 具体内容请移步官网Api文档 http://www.supervisord.org/api.html
87 |
88 | # 脚本
89 | 最后送给朋友们一个简单的脚本,方便用于学习Supervisor
90 | ```shell
91 | #!/bin/bash
92 | set -x
93 |
94 | case $1 in
95 | 'sp')
96 | if [[ $2 == 'start' ]]; then
97 | "supervisord -c /etc/supervisor/supervisord.conf"
98 | elif [[ $2 == 'stop' ]]; then
99 | "supervisorctl shutdown"
100 | elif [[ $2 == 'restart' ]]; then
101 | "supervisorctl shutdown"
102 | "supervisord -c /etc/supervisor/supervisord.conf"
103 | elif [[ $2 == 'reload' ]]; then
104 | "supervisorctl reload"
105 | fi
106 | ;;
107 | esac
108 | ```
109 | 你可以使用这个简单的脚本快速启动、重启、关闭Supervisor
110 | ```
111 | sh test.sh sp start // 启动
112 | sh test.sh sp restart // 重启
113 | ```
114 | # 致谢
115 | 感谢看到这里,希望本章可以帮到你。谢谢
--------------------------------------------------------------------------------
/PHP/PHP程序员如何简单的开展服务治理架构(三).md:
--------------------------------------------------------------------------------
1 | 服务治理所治理的服务需要合理的部署与管理,本章我们讲一下SOA(面向服务架构),本人语言文笔不好,所以本章内容使用问答模式,参考了 [SOA面试题(http://www.jdon.com/soa/soa-interview.html)] 的面试题,通过对此站复杂的描述进行简单的讲解。
2 |
3 | # 概述
4 | SOA代表了面向服务架构,仅仅是一种概念,通过这种概念而演变出的各种各样的服务架构都可称为SOA架构,SOA核心的概念就是 “松耦合”。
5 |
6 | # 非SOA的架构
7 | ### 多语言开发
8 | 
9 | ### 同语言开发
10 | 
11 |
12 | 服务与服务之间可能会是不同的开发语言或相同语言开发,他们的调用方式依旧只可以通过http去获取,或者比较流行的Restful Api的形式,无论是在性能与开发的过程中都是很笨的办法。
13 |
14 | # 什么是SOA的服务
15 |
16 | 
17 |
18 | 在现实世界中,服务是一种我们花费购买到的一种预期的服务。
19 | > 1、(来自真实世界):你去餐馆订餐,您的订单首先进入到柜台,然后在厨房进行食物准备,最后服务员提供的食物。因此,为了实现一个餐厅订购服务,您需要三个逻辑部门/服务协同工作(计帐,厨房和服务员)。在软件世界同样的方法称为业务服务。
20 |
21 | > 2、(软件世界):你去亚马逊订购了一本书,有不同的服务,如支付网关,库存系统,货运系统等共同完成一本书的订购。
22 |
23 | 
24 |
25 | 所有的服务是自包含的,合乎逻辑。他们就像黑盒子。总之,我们并不需要了解业务服务的内部工作细节。对于外部世界,它只是一个能够使用消息交互的黑盒子。例如在“支付网关”业务服务获得消息“检查信贷”后会给出输出:这个客户的信贷有或没有。对于“订单系统”,“支付网关”的服务是一个黑盒子。
26 |
27 | # 服务的主要特点是什么
28 | A) SOA组件是松耦合的。当我们说松耦合,这意味着每一个服务是自包含单独存在的逻辑。举例来说,我们采取了“支付网关”的服务,并将它附加到不同的系统。
29 |
30 | B) SOA服务是黑匣子。在SOA中,服务隐藏有内在的复杂性。他们只使用交互消息,服务接受和发送消息。通过虚拟化一个服务为黑盒子,服务变得更松散的耦合。
31 |
32 | C) SOA服务应该是自定义: SOA服务应该能够自己定义。
33 |
34 | D) SOA服务维持在一个列表中: SOA服务保持在一个中央存储库。应用程序可以在中央存储库中搜索服务,并调用相应服务。
35 |
36 | E) SOA服务可以编排和链接实现一个特定功能: SOA服务可以使用了即插即用的方式。例如,“业务流程”中有两个服务“安全服务”和“订单处理服务” 。从它的业务流程可以实现两种类型:一,您可以先检查用户,然后处理订单,或反之亦然。是的,你猜对了,使用SOA可以松散耦合的方式管理服务之间的工作流。
37 |
38 | # 什么是SOA
39 | SOA代表了面向服务的架构。 SOA是一种使用松耦合的黑盒子服务构建业务应用的体系架构,这些服务可以通过编排连接在一起以实现特定的功能。
40 |
41 | # 什么是合同,地址和绑定?
42 | 这是三个SOA的标准术语。每个服务对外开放地址,在服务开发中进行合同约定,客户端绑定服务进行开发调用。
43 |
44 | > * 合同是两方或多方之间的协议。它定义了一种客户端如何与服务通信的协议。从技术上讲,它有描述参数和返回值的方法。
45 | > * 地址表明在哪儿能找到这种服务。地址是一个URL,它指向服务的位置。
46 | > * 绑定是决定这个端点如何可以访问。它决定了如何完成通信。例如,你暴露你的服务,可以使用SOAP over HTTP或通过TCP的BINARY进行访问。因此,对于这些通信介质将被创建两个绑定。
47 | n
48 |
49 | # 什么是可重用的服务?
50 | > 服务是一个自主的,可重复使用的,可发现的,无状态的,有一定粒度的功能,并且是一个复合应用程序或一个组合服务的一部分。
51 |
52 | > 可重复使用的服务通过业务活动标识,这个业务活动是使用服务规范(设计时合同)描述的。
53 |
54 | > 一个服务约束是,包括安全性,QoS,SLA,使用策略,可以由多个运行时的合同 多个接口(WSDL中的Web服务)以及多个实现(代码)定义的。
55 |
56 | > 可重复使用的服务应在被管制在其从设计到运行整个企业级生命周期。其重用应通过规范流程来推动,重用应该是可测量的。
57 |
58 | # 在一个SOA中如何实现松耦合?
59 | > 实现松耦合一种策略是使用服务接口(WSDL中为SOAP Web服务)来限制服务之间的依赖性,对消费者隐藏服务实现。松耦合可以通过实施服务的功能封装以及限制服务接口的实现变化影响来解决。然而,在某些时候,你需要改变接口,也不会影响服务的消费者,除了管理多个安全约束,多种传输,以及其他方面的考虑。
60 |
61 | # SOA的服务无状态或有状态?
62 | > 服务应该是无状态的。它有一个无状态的执行上下文,但它不会有中间状态来等待一个事件或一个回调。状态有关的数据的保留一定不能超出的服务的请求/响应。这是因为状态管理消耗了大量的资源,这可能会影响服务的可重用 可伸缩性和可用性。
63 |
64 | 在RPC服务启动后,服务一直保持沉睡状态,只有在有请求时才会唤醒,你可以称他为无状态或有状态
65 |
66 | # 在SOA中我们是否需要从头开始构建系统?
67 | > 否。如果您需要集成现有系统为业务服务,你只需要创建松耦合的包装,包装您的现有系统,并以一种通用的方式暴露功能给外部世界。
68 |
69 | 其实并不需要重新构建,只需要将每个服务继续分解,分类出对外与对内。
70 |
71 | 
72 |
73 |
74 | # 什么是服务和组件之间的区别?
75 | > 服务组件来实现业务功能的逻辑组件分组。组件是为实现服务这个目标的途径。组件可以使用Java,C#,C,但服务将以通用格式如像Web服务方式被暴露。
76 |
77 | 说的就是我们通过RPC调用其他服务 (thrift)
78 |
79 |
80 | # 预告
81 | 看到这里大概了解了SOA,这个时候我透露一个秘密,SOA其实还有另外一个名字叫“**服务治理**”,是的,就是我们一直在讲的服务治理。下一章回到正题,讲一下服务治理实现
82 |
83 | PHP程序员如何简单的开展服务治理架构(一)
84 | https://segmentfault.com/a/1190000013481688
85 |
86 | PHP程序员如何简单的开展服务治理架构(二)
87 | https://segmentfault.com/a/1190000013544601
88 |
89 | 本猿人写了一个服务治理的框架
90 | https://github.com/CrazyCodes/Service-Govern
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/浅谈架构是为了什么 (下).md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 上一章对架构进行了通俗的解释,本章以图文并茂的形式对架构的演变做详细的阐述
5 |
6 | > 架构并非因高并发、大数据而生,以下的架构方式是根据业务演变而变更。
7 |
8 | 从现在开始,假设我们自己是一个创业的小团队。没资金没人脉,靠技术打天下。现在要开发一套电商系统。开始自己的表演。
9 |
10 | # 1.0
11 | 没钱没人,只能买得起一台阿里云学生机。这时我们只能选择使用下面的架构
12 |
13 | 
14 |
15 | 单机部署整个LNMP环境是唯一选择,这时我们还可以对1.0版本做一些优化的地方,在主从数据库这里,要注意了。单机跑主从简直是多此一举。单机流量本身就有效。主与从的承受是一致的。所以主从在单机跑?(相信你不是在开玩笑),但如果视数据与声明的话,主从还是有必要的(当备份了呗)。优化后的架构图如下
16 |
17 |
18 | 
19 |
20 | 虽然依旧很勉强,但我们将文件存储转移到了其他云,比较少于n个g流量是免费的😄,对php进行了优化,改修改的都改了,主从也做了。这个1.0的版本勉强的撑过了创业初期。然后灾难再一次降临...
21 |
22 | # 1.*
23 | 这时候手头已经有点钱了。公司准备再购入一台服务器。这时架构如下所示
24 |
25 | 
26 |
27 | 新购入的机器与老机做了一个负载均衡,将流量分发。这时候主从数据库就派上了很大的用场。这里先将主数据库作为增删改数据库,而从数据库而是查数据库。接下来可以安逸一段时间了。但在生产环境上,我们需要预知问题,检测bug,所以无奈下又改善架构
28 |
29 | 
30 |
31 | 将日志,包括mysql慢日志,查询日志,nginx的请求日志,错误日志,php的错误日志,慢日志都暂时存入redis内,至少我存起来了。有问题我能有处可查。随后将大表切分,例如文章表,用户表的。1.x的版本再不断迭代中越来越完善。但一旦出现并发就挂掉。这事看来需要认真的去对待了。
32 |
33 | # 2.0
34 | 谈及并发,我了解的并不深入,仅仅知道是一瞬间对服务器的冲击数,解决并发的办法也很简单,将请求分流,分的越细越好(这也并不是越多越好,具体界限,我也不清楚),架构图如下
35 |
36 | 
37 |
38 | 将用户请求(当然我指的是部分的),例如抢红包,限时抢购等业务,加入到队列中,挂为待处理形态,处理结束后将处理结果存储到redis然后通知用户,定时某个时段将redis数据存储到mysql,结束战斗。我想象的我们的公司已经比较牛x了。购入了很多台服务器。时候对现有的架构做出一些改变了。但并不是天翻地覆的变化。
39 |
40 | 
41 |
42 | 当然这仅仅是物理架构,真实的业务要复杂一些。接下来通过不断的努力,我们开启了2.x时代。
43 |
44 |
45 | # 2.*
46 | 应该具备的设备及其环境都准备好了。现在我们需要做的是将业务需求补齐,当然也没有想象的那么夸张。
47 |
48 | 
49 |
50 | 我们负载均衡作为分发调节器,将请求平均到指定服务器,通过开发语言去查询数据库数据然后返回,这一系列的操作都在监控系统与日志系统的监控中,当然说是系统,实际就是一个后台,一套程序,去监控数据时做筛选。如遇紧急情况则报警。数据正常并不是直接存储到数据库而是扔到队列,任由它翱翔。当业务不断壮大。其缺点或者漏洞并不是某种架构方案可以去解决,要就事论事,瞧病就医。这才是架构师的精髓所在。以上描述的这些套路。不好意思的说已经差不多吸光了我的架构知识库。下面的东西是正在研究的架构。
51 |
52 | # 3.0
53 | 我们预计上线的3.0版本引用了服务治理的架构思想。将业务分割,在本地通过tcp直接请求,并非通过http。这块我写过几篇文章,@周梦康 康神也有不错的直播讲解。以下为链接。我就不过多讲解了。
54 |
55 | PHP程序员如何简单的开展服务治理架构(一) [https://segmentfault.com/a/1190000013481688](https://segmentfault.com/a/1190000013481688)
56 |
57 | PHP程序员如何简单的开展服务治理架构(二) [https://segmentfault.com/a/1190000013544601](https://segmentfault.com/a/1190000013544601)
58 |
59 | PHP程序员如何简单的开展服务治理架构(三) [https://segmentfault.com/a/1190000013624228](https://segmentfault.com/a/1190000013624228)
60 |
61 | PHP 进阶之路 - 零基础构建自己的服务治理框架(上) [https://segmentfault.com/l/1500000011300619](https://segmentfault.com/l/1500000011300619)
62 |
63 | PHP 进阶之路 - 零基础构建自己的服务治理框架(下)[https://segmentfault.com/l/1500000011301018](https://segmentfault.com/l/1500000011301018)
64 |
65 | # 3.*
66 | 3.x是一个学习参考,自我反省,自我检讨的过程。参考大厂的架构设计,结合自己公司的架构设计,取其精华、去其糟粕。贴一张大厂的架构图我感觉毫无意义。这里就不多讲了。
67 | # 老一套
68 | 现在回到文章的中心思想上。到底为了什么做架构?我的答案是 “活” ,为了让产品活下去而做架构。什么时候可以做架构? 任意时间都可以做。但要看精力、财力、人力。
69 | ## 可扩展
70 | 可扩展性上篇文章我说过,是一把束缚我的刀,这把刀是什么?是“灾难”,在设计架构,包括在业务,物理或者是说部署上。这把刀一直在我脖颈处,如果这时为了省事,躲开了,那未来的某个时间,这把刀就会断头。做了五年的程序员。实际困难需求、复杂需求还有部分BUG的产品,个人认为与扩展脱不了关系。无论在任何的架构设计上,要考虑向前扩展、向后迭代。
71 | ## 可跑路
72 | 做好架构,写好代码。方能轻松跑路,否则后患无穷。
73 |
74 | # 致谢
75 | 感谢你看到这里,希望本篇可以帮到你。有问题可在评论区留言,谢谢 🙏
--------------------------------------------------------------------------------
/Go/PHP To Go 转型手记 (终).md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 作为一名PHP程序员,我感到荣幸。但在时代不断的变迁中,要具备足够的知识才可生存。
5 |
6 | 那就从Go语言学起把。
7 |
8 | 希望看到本篇文章的你可以对Go有一个基本的认识。本系列文章与我自己学习Go语言的方式去描述。以PHP代码与Go代码的对比加以区分理解。
9 |
10 | 这是转型手记的最后一章,在往下没办法再去写了,需要详细的看书去了解。本章以实战方式来对比以下PHP与Golang的写法。
11 |
12 | 这里使用Laravel与Beego(基于Go开发的MVC框架)来演示
13 |
14 | # 安装
15 | Laravel
16 | ```
17 | // 通过composer直接安装,简单易用
18 | composer global require "laravel/installer"
19 | // 创建一个项目
20 | laravel new blog
21 | ```
22 | Beego
23 | ```
24 | // go自身就有包管理
25 | go get github.com/astaxie/beego
26 | // 创建项目也非常简单
27 | bee api blog
28 | ```
29 | # 目录结构
30 | Laravel
31 | ```
32 | // laravel 的结构这里就不再阐述
33 | | - app
34 | | - bootstrap
35 | | - config
36 | | - database
37 | | - public
38 | | - resources
39 | | - routes
40 | | - storage
41 | | - tests
42 | | - vendor
43 | ```
44 | Beego
45 | ```
46 | // 显而易见,beego并没有laravel那样过度设计(虽然过度设计并非指目录,
47 | // 但以看目录就知道beego真的没有太多东西)
48 | blog
49 | ├── conf
50 | │ └── app.conf
51 | ├── controllers
52 | │ └── object.go
53 | │ └── user.go
54 | ├── docs
55 | │ └── doc.go
56 | ├── main.go
57 | ├── models
58 | │ └── object.go
59 | │ └── user.go
60 | ├── routers
61 | │ └── router.go
62 | └── tests
63 | └── default_test.go
64 | ```
65 | # 路由
66 | Laravel
67 | ```
68 | Route::get('/user', 'UserController@index');
69 | ```
70 | Beego
71 | ```
72 | // 与laravel的使用方式差不多
73 | // 这里为了统一,路由直接绑定控制器方法只有下列这种
74 | // beego 还提供了注解方式 , 详情见 https://beego.me/docs/mvc/controller/router.md
75 | beego.Router("/user",&UserController{},"get:index")
76 | ```
77 | # 模型 (Model)
78 | Laravel
79 | ```
80 | User::findOrFail($id)]);
136 | }
137 | }
138 | ```
139 | Beego
140 | ```
141 | package controllers
142 |
143 | import (
144 | "github.com/astaxie/beego"
145 | "github.com/astaxie/beego/orm"
146 | )
147 |
148 | // 这里相当于继承了父类 beegoController
149 | type MemberController struct {
150 | beego.Controller
151 | }
152 |
153 |
154 | func (c *CityController) Index() {
155 | var results []orm.Params
156 |
157 | orm.NewOrm().QueryTable("member").
158 | Values(&results)
159 |
160 | c.Data["json"] = results
161 | c.ServeJSON()
162 | }
163 | ```
164 | # 总结
165 | 学Go有1个多月的时间了。写这类的文章也遭到了很多质疑,作为一个手记去发布仅仅为了让想去学习其他语言的朋友了对新语言的一个认识,不是去对比其不同。而找其语法相似点。
166 |
167 | 语言只是工具,希望各位PHP工程师不要仅限于去使用PHP,这一年看到很多写PHP程序员未来之路一类的文章,但从未提出要去学习其他语言。
168 |
169 | 不要将自己圈在一个领域,不要做井底之蛙。
170 | # 致谢
171 | 感谢你看到这里,希望本篇文章可以帮到你。谢谢
--------------------------------------------------------------------------------
/PHP/老项目重构手记之用户系统.md:
--------------------------------------------------------------------------------
1 | > 受邀来一起重构公司的老项目
2 | # 概述
3 | 重构首先要注意几个点
4 | - 重构后功能的可扩展性
5 | - 业务互相依赖的复杂度
6 | - 脱离本身的业务进行重构
7 | - 重构后的代码可读性与可维护性
8 | - 性能的提升
9 | 以上几点是重构注意的地方也是重构的目的
10 |
11 | # 分析
12 |
13 | 本次重构的项目运营了三年之久,用户及业务量也上不来。至于重构的真正原因不清楚。
14 |
15 | - 用户注册量:107470
16 | - 日PV:1000+
17 |
18 | 非常的惨淡
19 |
20 | 1. 关于用户ID与其他业务绑定仅仅是单纯的存储用户ID进行绑定,类似与评论,购买等。这样在重新设计用户表的时候无需考虑其他表的业务是否有冲突或者依赖。
21 | 2. 前期设计上貌似接口及数据表字段设置问题,出现了数据重复的问题。
22 | 3. 功能重新写好后,在数据迁移方便,当然没法人工操作,php脚本去迁移也不现实,考虑使用数据队列等等方式进行数据迁移
23 | 4. 功能代码绝笔是另起炉灶写,在原程序上写复杂度有提升了一倍。
24 |
25 |
26 | # 原数据表结构
27 |
28 | > 部分字段
29 |
30 | | 字段名 | 类型 | 是否为空| 默认值 | 注释 |
31 | | -- | -- | -- | -- | -- |
32 | |MemberId | bigint(20) | Y | | 自增编码 |
33 | | MemberPhone | varchar(255) | N | '' | 手机号码 |
34 | | LoginTime | int(11) | Y | NULL | 登录时间 |
35 | | QuitTime | int(11) | Y | NULL | 退出时间 |
36 | | LoginState | tinyint(2) | Y | NULL | 登录状态 |
37 | | MemberState | tinyint(2) | N | 0 | 账号状态 |
38 | | MemberExpert | tinyint(2) | N | 0 | |
39 | | MemberRegTime | int(11) | Y | NULL | 注册时间|
40 | | DeviceToken | varchar(255) | Y | NULL | 设备标示 |
41 | | WxToken | char(32) | Y | NULL | 微信标示 |
42 |
43 | 问题点有以下几个
44 | 1. 数据字段设计时不应使用驼峰命名,应使用小写,单独分割用_ ,例如 member_tel , 索引的设置也存在一些问题
45 | 2. 字段尽量避免DEFAULT NULL
46 | 3. 根据需求分表,现在所有的第三方授权都放到一个表里了
47 |
48 | # 选型
49 | 前期重构要求速度要快。所以只能选择世界上最好的语言。
50 |
51 | - 语言:PHP
52 | - 框架:Laravel
53 | - 数据库:MySQL
54 |
55 | > 考虑到数据量也不小,手动操作是不可能了,选择使用RabbitMQ进行数据迁移
56 |
57 | # 新表设计
58 |
59 | ## 用户表
60 | ```
61 | CREATE TABLE `member` (
62 | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
63 | `tel` bigint(20) DEFAULT NULL COMMENT '手机号码',
64 | `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '登录密码',
65 | `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '账号状态 0:正常',
66 | `created_at` timestamp NULL DEFAULT NULL,
67 | `updated_at` timestamp NULL DEFAULT NULL,
68 | PRIMARY KEY (`id`),
69 | UNIQUE KEY `member_tel_unique` (`tel`),
70 | KEY `member_tel_status_index` (`tel`,`status`)
71 | ) ENGINE=InnoDB AUTO_INCREMENT=80073 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
72 | ```
73 |
74 | ## 用户授权表
75 | ```
76 | CREATE TABLE `member_authorized` (
77 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
78 | `member_id` bigint(20) NOT NULL COMMENT '用户编码',
79 | `prefix` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '第三方名称',
80 | `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '第三方标示',
81 | `data` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '授权获得的用户信息',
82 | `created_at` timestamp NULL DEFAULT NULL,
83 | `updated_at` timestamp NULL DEFAULT NULL,
84 | PRIMARY KEY (`id`),
85 | KEY `member_authorized_prefix_index` (`prefix`)
86 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
87 | ```
88 |
89 | ## 用户信息表
90 | ```
91 | CREATE TABLE `member_data` (
92 | `member_id` bigint(20) NOT NULL COMMENT '用户编码',
93 | `sex` enum('0','1','2') COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '性别 0=>女生 1=>男生 2=>未知',
94 | `nick_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '姓名/昵称',
95 | `img` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户头像',
96 | `created_at` timestamp NULL DEFAULT NULL,
97 | `updated_at` timestamp NULL DEFAULT NULL,
98 | UNIQUE KEY `member_data_member_id_unique` (`member_id`)
99 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
100 | ```
101 | # 队列
102 | 通过队列,可以选择laravel内置的队列或者rabbitmq都可以。将数据迁移到新表中。当然你需要选择一个访问量最低的时间段。并不是凌晨就少,不同的行业的活跃时间段不一样。建议先使用百度统计、腾讯分析等等的查看活跃时间区间。
103 | # 迭代
104 | 重构并不是一言一语,几行代码或者一个大佬的方案就可以解决的。实际重构也是一个开发的过程。在不断的迭代中,将重构完成的部分补回到业务中。
105 | # 致谢
106 | 感谢你看到这里,希望本篇文章可以帮到你。有问题可在评论区留言。
--------------------------------------------------------------------------------
/PHP/一道看似简单的面试题.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | > 使用PHP,给定一个数,判断这个数是否是二的N次方
5 |
6 | 这样看似简单的一个面试题, 实际牵出了很多基础知识,本章在为大家补习基础知识的情况下来解答这道题。先亮出答案
7 | ```
8 | function exponentiation($number)
9 | {
10 | if ($number < 0) {
11 | return false;
12 | }
13 |
14 | if (($number & ($number - 1)) == 0) {
15 | return true;
16 | } else {
17 | return false;
18 | }
19 | }
20 |
21 | exponentiation (2);
22 | exponentiation (8);
23 | exponentiation (16);
24 | exponentiation (32);
25 | exponentiation (128);
26 | ```
27 | 这是查阅后的最最标准的答案,其他类似通过循环等等答案就略过把。
28 |
29 | # 运算
30 | 实际这道面试题考的并非是算法,而是作为开发人员的你“底子”够不够。
31 | 运算符应该是每门语言hello world 后立马要学习的。运算符分很多种,赋值运算符,比较运算符等等。我想大多自学或者培训出道的没有经过系统化学习的童鞋的,对这块的知识应该很薄弱把。当然赋值、比较什么的很熟悉,因业务基本逃不过这些。但对位运算符,你真的吃透了吗?
32 |
33 | > 这道题的考点一是位运算符的使用,上面说过了本题考察的并非算法,而是你对二进制的了解,而在php中能操作二进制的运算符**貌似** (我的知识范围内) 只有位运算符
34 |
35 | ## PHP位运算符 (部分)
36 |
37 | 本道面试题用到了 & 所以不详解其他的运算符,需要更多了解请移步官方
38 | http://php.net/manual/zh/language.operators.bitwise.php
39 |
40 | | 运算符 |附加信息 |
41 | | -- | -- |
42 | | & | 按位与运算符|
43 | | ^ | 按位异或运算符 |
44 |
45 | ## 按位与
46 | 按位与以简单易懂的方式来讲就是二进制位不相同的抵消,相同的保留
47 |
48 | 
49 |
50 | 举几个栗子
51 |
52 | > 公式 -> 转为二进制后的公式 -> 二进制结果 -> 十进制结果
53 |
54 | ```
55 | 2 & 3 -> 0010 & 0011 -> 0010 = 2
56 | 10 & 7 -> 1010 & 0111 -> 0010 = 2
57 | 32 & 70 -> 0100000 & 1000110 -> 0000000 = 0
58 | ```
59 |
60 | ## 按位异或
61 | 按位异或以简单易懂的方式来讲就是二进制位相同的抵消,不相同的保留
62 |
63 | 
64 |
65 | 举几个栗子
66 |
67 | > 公式 -> 转为二进制后的公式 -> 二进制结果 -> 十进制结果
68 |
69 | ```
70 | 2 ^ 3 -> 0010 ^ 0011 -> 0001 = 1
71 | 10 ^ 7 -> 1010 ^ 0111 -> 1101 = 13
72 | 32 ^ 70 -> 0100000 ^ 1000110 -> 1100110 = 102
73 | ```
74 |
75 | # 进制
76 | 借用百度百科上的一段话
77 | > 二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”。
78 |
79 | 二进制本身就是为2这个数字而使用的,所以说这道面试题直指二进制的使用是没错的。2的n次方则就是
80 |
81 | | n |公式|结果|
82 | | -- | -- | -- |
83 | | 1 | 2| 2 |
84 | | 2 | 2x2 | 4 |
85 | | 3 | 2x2x2| 8 |
86 | | 4 | 2x2x2x2| 16 |
87 |
88 | 换算成二进制的表格是
89 | | n |公式|结果|
90 | | -- | -- | -- |
91 | | 1 | 2| 000010 |
92 | | 2 | 2x2 | 000100 |
93 | | 3 | 2x2x2| 001000 |
94 | | 4 | 2x2x2x2| 010000 |
95 |
96 | 由此看出2的n次方的二进制最高位是1,其余补0,(n&(n-1))==0 并且 n> 0 的情况下必定是2的n次方,为什么要-1呢?在二进制中每一位必须都不相同&后才会得出0,上述已经讲解了&的运算结果。例如十进制的16
97 | ```
98 | 16 & (16 - 1) =
99 | 010000 & (001111) = 0
100 | ```
101 | 不要纠结(n&(n-1))==0 这个公式是怎么来的,作为程序员,我感觉应该把更多时间放到反推上面来,去应证这个公式的正确性。
102 | > 验证是否是2的n次方,笨的方法就是一直除2,除到最后等于0则就是2的次方,所以公式如上
103 |
104 | # 补位
105 | 如果是2个二进制进行运算时,计算机会统一位数,例如
106 | ```
107 | 01
108 | 011
109 | <------>
110 | 001
111 | 011
112 | ```
113 | 计算机会将01自动补一位为001去方便运算。
114 |
115 | # 正负
116 | 在二进制中,第一位为1的是负数,0是正数。如果没有补零的情况下
117 | ```
118 | 10000000000
119 | 01111111111
120 | ```
121 | 虽然计算后也是0,但它并不是2的n次方,因为第一组二进制是负数。
122 |
123 | # 补充
124 | 由上述题补充的另外一道题
125 | > 给定任意数,计算是2的几次方?
126 |
127 | ```
128 | function power($number){
129 | if ($number < 0) {
130 | return false;
131 | }
132 |
133 | if (($number & ($number - 1)) == 0) {
134 |
135 | // 数学不好的,就看下面的方法
136 | // $number = decbin($number);
137 | // return (mb_strlen($number)-1);
138 | // 数学可以的就看下面的方法
139 | return floor(log($number,2));
140 | } else {
141 | return false;
142 | }
143 | }
144 | ```
145 | 1. 判断是否是2的n次方
146 | 2. 如果是则将十进制数字转为二进制
147 | 3. 计算总长度-1获取到是2的几次方,按照0的个数来计算
148 |
149 | # 致谢
150 | 感谢你看到这里,我也是文中提起的没好好学基础的一名程序员,但当你看到我这篇文章后,希望你也可以提起精神,去重温下基础,对你未来的职业生涯会起作用的。本章内容纯属自己理解,如有出入,请大佬们监督批评,谢谢🙏
--------------------------------------------------------------------------------
/PHP/2019 PHP程序员发展路线.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 新的一年,新气象。在2012年我参加LAMP兄弟连的培训,成为一名PHP程序员。那个时候PHP盛行,简直有称霸世界的迹象。当然现在我大PHP也不差。我认为成为PHP程序员的有以下三种途径。
5 |
6 | 1. 科班出身,找了一份PHP开发相关的工作
7 | 2. 自学成才 (毕竟在当时PHP入门是所有语言里最简单的)
8 | 3. 培训班出身 (就像我,不是科班,自学能力也不强,所以参加的培训学校 注:2012年的LAMP兄弟连其实不错的,现在的就不评论了)
9 |
10 | 就我这近六年的职业生涯。为大家准备了一份2019年程序员发展路线。
11 |
12 | # 跟上潮流
13 |
14 | 
15 |
16 | 2012年的PHP是web开发的强者,我记得当年的PHP微信开发简直是火到不行,经过这几年的不断发展,PHP实际更偏向后端了。我已经很久没有动过前端的东西了,当年都是混合开发乱的不行,所以作为一个PHP程序员不要太计较前端的那些技能,注重后端该会的东西。
17 |
18 | # 框架
19 |
20 | 
21 |
22 | - [Laravel][1] 一款过度设计的,优雅的,复杂的 PHP开发框架 , 这个框架在我多年实践中证明只适合写后台,如果用他写接口你会发现性能与原生PHP差距很大,具体比对数据可自行Google。建议把Laravel的设计方式认真学习一下,并非必须去学习使用这款框架
23 | - [Lumen][2] 这是一款Laravel的Api框架,其速度要比Laravel快很多,是一款精简的Laravel
24 | - [Symfony][3] 没怎么看过这款框架,Symfony即是一款框架,也是一组PHP组件库,要知道Laravel的DB,Dump,Route,Response其实都是在Symfony组件基础上做的。可见laravel composer.json https://github.com/laravel/framework/blob/5.7/composer.json
25 | - [CodeIgniter][4] 也可以关注下上个时代框架霸主,CodeIgniter 他的新版本可能会有奇迹发生
26 |
27 | # 扩展
28 |
29 | 
30 |
31 | - [swoole][5] PHP异步编程框架,这个就不必多说了。自4.1.0 Swoole加入Coroutine,使并发开发更简单。语法非常类似Goroutine
32 | - [rabbitmq][6] 消息队列,数据过多的时候就知道有什么用了
33 | - [docker][7] 不要告诉我2019年你还不听过docker,容器技术泛滥,该看下了老铁,https://segmentfault.com/a/1190000016436478 , https://segmentfault.com/a/1190000015407534
34 |
35 |
36 | # DevOps
37 | 
38 |
39 | 这是一个看起来高端但很切合实际的话题。如何做到DevOps? 可以先了解下下面的知识
40 |
41 | - [travisCi][8] 一款基于Github的自动发布,自动集成,自动测试的平台 ,https://segmentfault.com/a/1190000017742260
42 | - [teamcity][9] jetbrains推出的一款自动发布、集成、测试的平台,https://segmentfault.com/a/1190000017117035
43 | - [phpunit][10] 当然做前面两个之前你必须学会如何有效的写测试
44 | - [composer][11] 学习强有力的搬砖技巧,板巧砖,要学会找各种组件包去实现自己的应用
45 |
46 | # 算法
47 |
48 | 
49 |
50 | 算法是程序开发的基础,(大厂更看重基础),可以适当在下方平台去联系
51 |
52 | - [LintCode][12]
53 | - [力扣][13]
54 |
55 | 什么?上面的题根本做不出来?没思路?乱七八糟的一些算法书我就不推荐的,首先判定你与我当年一样 (我们数学就没学好),虽然计算机算法与数学有些许出入,不过还是建议继续看我下面的建议。
56 |
57 | # 基础
58 | 从小就不爱学习的我,选择了这个职业,无奈基础没打牢(实际就是没打),我选择这样强补知识。作为山东人(北方人),我选择了人教版《数学》,如果你有这样的勇气,那么跟我一起来补基础吧。
59 |
60 | 
61 |
62 | 我是从初中数学开始到高中数学。在学习的过程中买很多试卷做,巩固练习。在这之后再考虑大学期间学习的知识吧。其他相关阅读书籍可参考下方
63 |
64 | - https://segmentfault.com/a/1190000015424521
65 |
66 | 除了数学外,则应该是计算机相关的线程,通信协议等等....
67 |
68 | # 语言
69 |
70 | 
71 |
72 | 之所以把学习其他语言放到这里,是感觉并不是太重要,如果是一位长期战斗的程序员,我相信他的学习另外一门语言是手到擒来的。不过你也可以选择几门当做业余爱好。
73 |
74 | - Go
75 | - Java
76 | - C+
77 | - Python
78 |
79 | 随意选择,学什么语言都一样,不过只是学学语法而已(至少大多人都是这样)
80 |
81 |
82 | # 致谢
83 |
84 | 感谢你看到这里,希望2019年的你比2018年更上一层楼,希望我的文章可以从根本上帮助到你。谢谢
85 |
86 |
87 | [1]: https://laravel.com/
88 | [2]: https://lumen.laravel.com/
89 | [3]: https://symfony.com
90 | [4]: https://github.com/bcit-ci/CodeIgniter
91 | [5]: https://www.swoole.co.uk/
92 | [6]: https://www.rabbitmq.com/
93 | [7]: https://www.docker.com/
94 | [8]: https://travis-ci.org
95 | [9]: https://www.jetbrains.com/teamcity/?fromMenu
96 | [10]: https://phpunit.de/
97 | [11]: https://getcomposer.org/
98 | [12]: https://www.lintcode.com/
99 | [13]: https://leetcode-cn.com/
--------------------------------------------------------------------------------
/Docker/五分钟快速了解Docker.md:
--------------------------------------------------------------------------------
1 | > 这些是我的笔记,都是记录的核心概念和使用方法
2 |
3 | 
4 |
5 | # 概述
6 | 使用docker搭建开发环境将近1年了,自我感觉docker的强大并非如此,不过没有机会将docker部署生产环境,有位架构师曾说过,最新的未必是最好的,架构要选择最佳的,请大家也慎重。
7 |
8 | # 核心概念
9 | ## 镜像 Image
10 | Docker镜像类似于虚拟机镜像。每个镜像包括了一个基本的操作系统环境
11 |
12 | ## 容器 Container
13 | Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。可以把容器看做一个简易版的linux系统环境
14 |
15 | ## 仓库 Repository
16 | Docker仓库类似于代码仓库,它是Dokcer集中存放镜像文件的场所
17 |
18 | # 数据管理
19 | > 生产环境中使用Docker的过程中,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然要涉及容器的数据管理操作
20 |
21 | 容器中管理数据主要有两种方式:
22 | * 数据卷
23 | * 数据卷容器
24 |
25 |
26 | ## 挂载一个主机目录作为数据卷
27 | ```
28 | docker run -d -P --name web -v /localhost-path:/server-path NAME:TAG
29 | ```
30 |
31 | # 端口映射
32 |
33 | ## 端口映射实现访问容器
34 | ## 从外部访问容器应用
35 | > 要让外部访问这些应用时,可以通过-P或-p参数来指定端口映射。当使用-P标记时,docker会随机映射一个49000~49900的端口到内部容器开放的网络端口
36 | ```
37 | docker run -d -P NAME:TAG
38 | ```
39 | ## 映射所有接口地址
40 | ```
41 | docker run -d -p 5000:5000 NAME:TAG
42 | ```
43 | > -p 可以标记多个端口
44 | ```
45 | docker run -d -p 5000:5000 -p 3000:80 NAME:TAG
46 | ```
47 | ## 映射到指定地址的指定端口
48 | ```
49 | docker run -d -p 127.0.0.1:5000:5000 NAME:TAG
50 | ```
51 | ## 映射到指定地址的任意端口
52 | > 使用IP::ContainerPort 绑定localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口
53 | ```
54 | docker run -d -p 127.0.0.1::5000 NAME:TAG
55 | ```
56 | 还可以使用udp标记来指定udp端口
57 | ```
58 | docker run -d -p 127.0.0.1:5000:5000/udp NAME:TAG
59 | ```
60 | ## 查看映射端口配置
61 | ```
62 | docker port nostalgic_morse PORT
63 | ```
64 |
65 | # 容器互联
66 |
67 | ## 自定义容器命名
68 | > 使用--name标记可以为容器自定义命名
69 | ```
70 | docker run -d -P --name web NAME:TAG
71 | ```
72 | ## 查看容器名字
73 | ```
74 | docker inspect -f "{{ .Name }}" 容器id
75 | ```
76 |
77 | ## 容器互联
78 | > 使用--link参数可以让容器之间安全的进行交互
79 | ```
80 | docker run -d --name db NAME:TAG
81 | docker run -d -P --name web --link db:db NAME:TAG
82 | ```
83 |
84 | # 使用Docker镜像
85 | ## 下载镜像
86 | ```shell
87 | docker pull NAME[:TAG]
88 | ```
89 | ## 创建容器
90 | ```shell
91 | docker run -it NAME:TAG SERVICE
92 | ```
93 |
94 | ## 查看镜像
95 | ```shell
96 | docker images
97 | ```
98 | * REPOSITOPY 来自于哪个仓库
99 | * TAG 镜像的标签信息
100 | * IMAGE ID 镜像的ID
101 | * CREATED 创建时间
102 | * SIZE 镜像大小
103 |
104 | ## 给镜像打标签
105 | ```shell
106 | docker tag NAME:TAG NEWNAME:TAG
107 | ```
108 |
109 | ## 查看镜像详细信息
110 | ```shell
111 | docker inspect NAME:TAG
112 | ```
113 |
114 | ## 查看镜像历史
115 | ```shell
116 | docker history NAME:TAG
117 | ```
118 |
119 | ## 搜索镜像
120 | ```shell
121 | docker search NAME
122 | ```
123 |
124 | ## 删除镜像
125 | ```shell
126 | docker rmi IMAGE
127 | ```
128 |
129 | ## 删除容器
130 | ```shell
131 | docker rm 容器ID
132 | ```
133 | ## 创建镜像
134 |
135 | ## 基本已有镜像的容器创建
136 | ```
137 | docker commit [OPTIONS] CONTAINER [REPOSITOPUY[:TAG]]
138 |
139 | -a , --author="" // 作者信息
140 | -c , --change=[]
141 | -m , --message="" // 提交消息
142 | -p , --pause=true // 提交时暂停容器运行
143 |
144 | docker commit -m "added a new file " -a "crazy" 58fe3bd5b3e6 test:0.1
145 | ```
146 |
147 | ## 基于本地模板导入
148 | ```
149 | cat 本地包 | docker import - 导入的镜像名称
150 | ```
151 |
152 | ## 导出镜像
153 | ```
154 | docker save -o 导出文件名 导出的镜像
155 | ```
156 | ## 载入镜像
157 | ```
158 | docker load --input 本地镜像名称
159 | // 或者
160 | docker load < 本地镜像名称
161 | ```
162 | ## 查看镜像详细信息
163 | ```
164 | docker inspect NAME:TAG
165 | ```
166 |
167 | # Docker相关的快捷命令
168 |
169 | ## 停用全部运行中的容器:
170 | ```
171 | docker stop $(docker ps -q)
172 | ```
173 | ## 删除全部容器
174 | ```
175 | docker rm $(docker ps -aq)
176 | ```
177 |
178 | ## 一条命令实现停用并删除容器
179 | ```
180 | docker stop $(docker ps -q) & docker rm $(docker ps -aq)
181 | ```
182 | ## 删除所有未打 tag 的镜像
183 | ```
184 | docker rmi $(docker images -q | awk '/^
2 | # 前言
3 | 大家好,我是CrazyCodes,一名正在创业路上的程序员,今天我为各位整理2020年PHP程序员修炼秘籍,希望可以帮到你。
4 |
5 | # 语言
6 | 2019年是不安分的一年,身为程序员的我们也是恐慌不已,大厂各种裁员,整的程序员界人心惶惶。就算是这样,依旧有很多喷子攻击我大PHP阵营
7 |
8 | > 张三喷子:“php已经不行了”,马上要被什么什么替代了。
9 | > 李四喷子:“php这个技术不如这门语言了,不如那门语言了”
10 | > 王五喷子:“身边朋友都去学某某语言了,php不行了”
11 |
12 | 其实吧,作为多年php圈子里面的混混,我想说两句
13 |
14 | 首先,奉上最新的世界开发语言排行榜
15 |
16 |
17 |
18 | 至今为止,其实PHP没有跑出前十名,其实会不会跑出前十名这并不重要。JavaScript在PHP前面,也并不会代表什么。
19 |
20 | 发表下个人意见吧
21 | 1. 开发语言的存在即合理
22 | 2. PHP算是一门老语言,在互联网发展长河里,PHP语言承担着历史上的重要
23 | 3. 无论出现什么形式的新开发语言,都无法代替PHP本身在界内的影响力
24 |
25 | # 开发框架
26 | 2019年并没有出现特别火爆的新开发框架。
27 | 位于榜首的依旧是万能脚手架 - Laravel
28 |
29 | 
30 |
31 | 在过去的一年,我见过太多太多PHP的项目在重构过程中慢慢引进Laravel,并将Laravel作为项目首选开发框架。
32 |
33 | 当然我也是如此,作为Laravel框架的早期使用者,我由衷的感觉Laravel在近些年内的地位还是很稳定的。
34 |
35 | 有些人要说了,Laravel框架太重、运行速度太慢、blblblbl的各种原因,我只能说,羊毛出在羊身上,Laravel是组件化开发早期框架,感觉哪个慢就不加载哪个呗,哪个重就删掉不就行了,一天天不找解决方案,只会抛出问题,难道Laravel框架的开发者会按照小众个人意愿去改嘛😆
36 |
37 | 其他的框架我很少了解,在这里就着重讲解下Laravel
38 |
39 | 框架本身没什么好说的,讲些Laravel社区生态相关的事。
40 |
41 | ## 文档
42 | 首先说的是完整的框架中文相关文档,这里必须提到一个大佬Summer,是这位大佬建立起Laravel中文社区,让所有Laravel框架使用者不再对英文发愁
43 | https://learnku.com/laravel
44 |
45 | ## 开发
46 | 让开发者更快速的构建属于自己的PHP程序,Laravel做了很多。例如一键启动PHP程序
47 | ```
48 | php artisan serve
49 | ```
50 | 这让PHP程序员在任意的一台电脑上都可以轻松编写PHP代码并直接运行
51 |
52 | 当然还有专属于Laravel的开发环境 Homestead
53 |
54 | ## 扩展
55 | 最后是各种第三方扩展不断丰富着Laravel社区,例如
56 |
57 | Horizon,它为Laravel提供了队列可视化的仪表盘,为什么列出Horizon呢,是因为对于程序员来说,看不到或者无法断言的任何事情,会让程序员没有安全感,而恰恰Horizon让你看到所有你想看到的事件
58 |
59 | 
60 |
61 | 还需要强调的一个Telescope,我现在的项目内也使用了Telescope
62 |
63 | 
64 |
65 | 它会检测框架内所有的请求,并列出相关信息,当程序出现问题的时候,你会发现Telescope简直是救命神器
66 |
67 | 还有很多很多的工具等待你的使用
68 |
69 |
70 |
71 | 当然选择什么框架,用什么方式来去做PHP语言的相关开发,还是要根据需求和个人喜好去选择。都是PHP大营的产品,这里不做太多描述。(怕被自己人打😆)
72 |
73 | # Swoole
74 | 
75 | Swoole算是重新托起了PHP在开发语言中的地位。将以往无法实现或者实现困难的问题几乎全部解决。
76 |
77 | Swoole已经众所周知,本篇就不过多描述了。
78 | https://www.swoole.com/
79 |
80 | # 工具
81 | 众所周知,在开发过程中,仅仅熟练使用自己的本命语言是完全不够的。还要依托一些三方工具,去不断提升开发效率和编码质量。
82 |
83 | ## Sequel Pro
84 |
85 |
86 | Sequel Pro 是一款数据库管理软件,在使用过不会因数据量过大而卡死,UI精美小巧。是我这些年来一直使用数据库管理软件。
87 |
88 | ## VLC
89 |
90 | 这是一款测试推拉流的工具,至今是没有找到比它更好的软件代替
91 |
92 | ## PhpStorm
93 | 每位程序员都有自己钟爱的开发工具,PhpStorm则是我的首选。可能本身体积略大,耗费内存也比较高,对电脑本身是有一些要求的。
94 |
95 | 但当你真正熟练使用它的时候,会发现开发速度不是一般的快。
96 |
97 | ## ShadowsocksX
98 |
99 | 这个少说为好,如果是一名真正的程序员,我相信翻qiao对TA来说比媳妇还重要
100 |
101 | ## PostMan
102 |
103 | 在移动互联网时代,每位PHP程序员都必须要开发接口。而PostMan在开发过程中作为调试神奇,是每位程序员装机必备之佳品
104 |
105 | ## 有道云笔记
106 |
107 | 其实吧,这个也属于程序员必备。人生漫漫,在从事开发事业的过程中,要记录的东西还是很多的吧。
108 |
109 | ## Alfred
110 |
111 | Mac必备神器,当用了它后,我感觉生活水平都提高了😆
112 |
113 | ## GitKraken
114 |
115 | GitKraken是我迄今为止见过ui做的最美的git客户端
116 |
117 | 还有很多很多工具有待发现,当然一把趁手的刀才是好刀,各位朋友按需选择
118 |
119 | # 前端
120 | 简单聊一下前端,近些年来,前端发展迅速,我认为作为一名程序,无论是做前端、后端、服务端还是PC端开发的。都应去了解其“火”的原因,去了解,去实践,不要被时代抛弃。
121 |
122 | 前端技术我的建议是:还是需要去学习的,要不要深学要看个人需求
123 |
124 | # 最后
125 |
126 | 当我发出思否[2019年总结文章](https://segmentfault.com/a/1190000021420447)的时候,相信大家已经知道我不再是公司员工了。
127 |
128 | 对于创业,我只想说,提升自己全方面的能力,不仅仅是技术大拿那么简单的事情了。创业是另外一种生活方式,如要选择,便是不归路。
129 |
130 | 各位准备创业的程序员朋友,请三思而后行。
131 |
132 | 顺便宣传下我创业的产品,是一款习惯养成的APP,多年来我都是严格要求自己,去年我做了这款产品,希望可以让更多的人自律起来。真正的自由,是自律带给你的选择权
133 |
134 |
135 |
136 | # 致谢
137 | 感谢你看到这里,希望本篇文章可以帮助到你,谢谢。
--------------------------------------------------------------------------------
/优化你的PHP代码,从现在做起.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | > 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes/Blog
5 |
6 | 大家好,我是CrazyCodes ,今天我们不聊工具、规范等等等等的辅助,就聊一下该如何写一段“好”的代码,本文以我的职业生涯碰到的代码为例,如有出入请在评论区提出异议,谢谢。
7 |
8 | # 搜索功能
9 | 搜索很常见,复杂的搜索大多出行在后台,举个栗子,大概需求是这样的
10 |
11 | **这是一个后台用户列表的搜索功能**
12 | | 搜索条件 | 可否并行 | 是否必填 |
13 | | -- | -- | -- |
14 | | 用户名 | 可以 | 否 |
15 | | 手机号码 | 可以 | 否 |
16 | | 是否已认证 | 可以 | 是 |
17 | | 用户性别 | 可以 | 否 |
18 | | 最近登录时间 | 可以 | 否 |
19 | | 账户余额 | 可以 | 否|
20 |
21 | # 初学者代码
22 | 看到这些例子你是否不由的一颤,又要开始造轮子的是不是?
23 | 以原生的例子为例,开始你可能会这样写(以下为伪代码)
24 | ```
25 | if (IS_POST) {
26 | $like = '';
27 | if (isset($_POST['username'])) {
28 | $username = $_POST['username'];
29 | $like .= "username like '%" . $username . "%' and ";
30 | }
31 |
32 | if (isset($_POST['phone'])) {
33 | $phone = $_POST['phone'];
34 | $like .= "phone like '%" . $phone . "%' and";
35 |
36 | }
37 |
38 | if ($_POST['is_auth']) {
39 | $isAuth = $_POST['is_auth'];
40 | $like .= "is_auth like '%" . $isAuth . "%' and";
41 |
42 | }
43 |
44 | if ($_POST['sex']) {
45 | $sex = $_POST['sex'];
46 | $like .= "sex like '%" . $sex . "%' and";
47 | }
48 |
49 |
50 | if ($_POST['time']) {
51 | $time = $_POST['time'];
52 | $like .= "time like '%" . $time . "%' and";
53 | }
54 |
55 |
56 | if ($_POST['wallet']) {
57 | $wallet = $_POST['wallet'];
58 | $like .= "wallet like '%" . $wallet . "%' and";
59 |
60 | }
61 |
62 | $like = rtrim($like, 'and');
63 |
64 | $sql = "SELECT * FROM `user` WHERE {$like}";
65 |
66 |
67 | } else {
68 | return view('user');
69 | }
70 | ```
71 | # 封装
72 | 恩...,还不错,结构清晰,传统的初学者条型代码,接下来我们先封装一下几块代码。
73 | ```
74 | function post($param)
75 | {
76 | return isset($_POST[$param]) ? $_POST[$param] : null;
77 | }
78 |
79 | if (IS_POST) {
80 | $like = '';
81 | if (post('username')) {
82 | $username = post('username');
83 | $like .= "username like '%" . $username . "%' and ";
84 | }
85 |
86 | if (post('phone')) {
87 | $phone = post('phone');
88 | $like .= "phone like '%" . $phone . "%' and";
89 |
90 | }
91 |
92 | if (post('is_auth')) {
93 | $isAuth = post('is_auth');
94 | $like .= "is_auth like '%" . $isAuth . "%' and";
95 |
96 | }
97 |
98 | if (post('sex')) {
99 | $sex = post('sex');
100 | $like .= "sex like '%" . $sex . "%' and";
101 | }
102 |
103 |
104 | if (post('time')) {
105 | $time = post('time');
106 | $like .= "time like '%" . $time . "%' and";
107 | }
108 |
109 |
110 | if (post('wallet')) {
111 | $wallet = post('wallet');
112 | $like .= "wallet like '%" . $wallet . "%' and";
113 |
114 | }
115 |
116 | $like = rtrim($like, 'and');
117 |
118 | $sql = "SELECT * FROM `user` WHERE {$like}";
119 |
120 |
121 | } else {
122 | return view('user');
123 | }
124 | ```
125 | # 适当使用迭代
126 | 恩,至少我们可以自由控制post方法了,但是这类过程化代码维护性太低,我们再改进下
127 | ```
128 | function post($param)
129 | {
130 | return isset($_POST[$param]) ? $_POST[$param] : false;
131 | }
132 |
133 | function postAll()
134 | {
135 | return $_POST;
136 | }
137 |
138 | if (IS_POST) {
139 | $like = '';
140 |
141 |
142 | foreach (postAll() as $key => $value) {
143 | if (post($key)) {
144 | $like .= "{$key} like '%{$value}%' and";
145 | }
146 | }
147 |
148 | $like = rtrim($like, 'and');
149 |
150 | $sql = "SELECT * FROM `user` WHERE {$like}";
151 |
152 |
153 | } else {
154 | return view('user');
155 | }
156 | ```
157 | # 面向对象
158 | 加个迭代代码看起来还算是整洁了点,作为PHP程序员,写代码不面向对象不靠谱,把class加上
159 | ```
160 | function request($param = null)
161 | {
162 | return new Request($param);
163 | }
164 |
165 | class Request
166 | {
167 | public function __construct(string $param = null)
168 | {
169 | return isset($_POST[$param]) ? $_POST[$param] : false;
170 | }
171 |
172 | public function all()
173 | {
174 | return $_POST;
175 | }
176 | }
177 |
178 |
179 | class User
180 | {
181 | public function index()
182 | {
183 | if (IS_POST) {
184 | $like = '';
185 |
186 | foreach (request()->all() as $key => $value) {
187 | if (request($key)) {
188 | $like .= "{$key} like '%{$value}%' and";
189 | }
190 | }
191 |
192 | $like = rtrim($like, 'and');
193 |
194 | $sql = "SELECT * FROM `user` WHERE {$like}";
195 |
196 | } else {
197 | return view('user');
198 | }
199 | }
200 | }
201 |
202 | ```
203 | # 对User的改造
204 | 我们在对User的类进行改造,做一些判断及筛选
205 | ```
206 | function request($param = null)
207 | {
208 | return new Request($param);
209 | }
210 |
211 | class Request
212 | {
213 | public function __construct(string $param = null)
214 | {
215 | return isset($_POST[$param]) ? $_POST[$param] : false;
216 | }
217 |
218 | public function all()
219 | {
220 | return $_POST;
221 | }
222 | }
223 |
224 |
225 | class User
226 | {
227 | public $request = [
228 | 'username',
229 | 'phone',
230 | 'is_auth',
231 | 'sex',
232 | 'time',
233 | 'wallet'
234 | ];
235 |
236 | public function index()
237 | {
238 | if (IS_POST) {
239 | $like = '';
240 |
241 | foreach (request()->all() as $key => $value) {
242 | if (in_array($key, $this->request) && request($key)) {
243 | $like .= sprintf("%s like %s and", $key, $value);
244 | }
245 | }
246 |
247 | $like = rtrim($like, 'and');
248 |
249 | $sql = "SELECT * FROM `user` WHERE {$like}";
250 |
251 | } else {
252 | return view('user');
253 | }
254 | }
255 | }
256 |
257 |
258 | ```
259 | 这就差不多了,对比真是的代码可能还相差甚远,我写这篇文章的目的不是教会你如何写代码,是想说明编码不是一次性的,应经过多次修改,使代码具有可维护性,扩展性等等的,各种“性”
260 | # 致谢
261 | 感谢你看到这里,希望本篇文章可以帮到你,谢谢
262 |
--------------------------------------------------------------------------------
/MySQL/MySQL常用系统表汇总.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 概述
4 | 本篇文章虽大部分内容为参考原文作者的相关内容,但对原文对于文章的逻辑与排版上进行了大范围修改,方便阅读与理解。原文链接在底部
5 |
6 | # MySQL5.7 默认模式
7 |
8 | | 库名 | 表数量 | 视图数量 |
9 | | ------------ | ------------ | ----|
10 | | information_schema | 61 | 0|
11 | | mysql | 32 | 0|
12 | | performance_schema | 87 | 0|
13 | | sys | 1 | 100|
14 |
15 | ## Information_schema
16 | Information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。
17 |
18 | ### 什么是元数据呢?
19 | 元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
20 |
21 | 在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件
22 |
23 | ### information_schema 数据库部分表说明
24 | | 表名 | 注释 |
25 | | ------------ | ------------ |
26 | | SCHEMATA | 提供了当前mysql实例中所有数据库的信息。是show databases的结果取之此表 |
27 | | TABLES | 提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema、表类型、表引擎、创建时间等信息。是show tables from schemaname的结果取之此表 |
28 | | COLUMNS | 提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。是show columns from schemaname.tablename的结果取之此表 |
29 | | STATISTICS |提供了关于表索引的信息。是show index from schemaname.tablename的结果取之此表 |
30 | | USER_PRIVILEGES | 用户权限表:给出了关于全程权限的信息。该信息源自mysql.user授权表。是非标准表|
31 | | SCHEMA_PRIVILEGES | 方案权限表:给出了关于方案(数据库)权限的信息。该信息来自mysql.db授权表。是非标准表 |
32 | | TABLE_PRIVILEGES | 表权限表:给出了关于表权限的信息。该信息源自mysql.tables_priv授权表。是非标准表 |
33 | | COLUMN_PRIVILEGES |列权限表:给出了关于列权限的信息。该信息源自mysql.columns_priv授权表。是非标准表 |
34 | | CHARACTER_SETS | 字符集表:提供了mysql实例可用字符集的信息。是SHOW CHARACTER SET结果集取之此表|
35 | | COLLATIONS | 提供了关于各字符集的对照信息|
36 | | COLLATION_CHARACTER_SET_APPLICABILITY |指明了可用于校对的字符集。这些列等效于SHOW COLLATION的前两个显示字段。 |
37 | | TABLE_CONSTRAINTS | 描述了存在约束的表。以及表的约束类型|
38 | | KEY_COLUMN_USAGE |描述了具有约束的键列 |
39 | | ROUTINES | 提供了关于存储子程序(存储程序和函数)的信息。此时,ROUTINES表不包含自定义函数(UDF)。名为“mysql.proc name”的列指明了对应于INFORMATION_SCHEMA.ROUTINES表的mysql.proc表列|
40 | | VIEWS |给出了关于数据库中的视图的信息。需要有show views权限,否则无法查看视图信息 |
41 | | TRIGGERS | 提供了关于触发程序的信息。必须有super权限才能查看该表 |
42 |
43 | ## performance_schema
44 | PERFORMANCE_SCHEMA这个功能默认是关闭的。需要设置参数: performance_schema 才可以启动该功能,这个参数是静态参数,只能写在my.cnf 中 不能动态修改。
45 | ### performance_schema数据库部分表说明
46 | | 表名 | 注释 |
47 | | ------------ | ------------ |
48 | | setup_table | 设置表,配置监控选项 |
49 | | current_events_table | 记录当前那些thread 正在发生什么事情 |
50 | | history_table | 发生的各种事件的历史记录表 |
51 | | summary_table | 对各种事件的统计表 |
52 | | setup_consumers\setup_instruments|描述各种事件, 设置哪些事件能够被收集 |
53 | | setup_instruments | 描述这个数据库下的表名以及是否开启监控 |
54 | | setup_timers | 描述监控选项已经采样频率的时间间隔|
55 | | threads |监控服务器所有连接 |
56 | | performance_timers | 设置一些监控信息, 指定mysql服务可用的监控周期,CYCLE表示按每秒检测2603393034次, 目前 performance-schema 只支持'wait'时间的监控,代码树上 wait/ 下的函数都可以监控到|
57 |
58 | ## mysql
59 | 在mysql数据库中,有mysql_install_db脚本初始化权限表,存储权限的表
60 | ### mysql数据库部分表说明
61 | | 表名 | 注释 |
62 | | ------------ | ------------ |
63 | | user | 用户列、权限列、安全列、资源控制列 |
64 | | db | 用户列、权限列 |
65 | | host | |
66 | | table_priv | |
67 | | columns_priv | |
68 | | proc_priv | |
69 |
70 |
71 |
72 | ## sys
73 | sys_config : 这是在这个系统库上存在的唯一一个表
74 | ### sys数据库表说明
75 | ```
76 | CREATE TABLE `sys_config` (
77 | `variable` varchar(128) NOT NULL,
78 | `value` varchar(128) DEFAULT NULL,
79 | `set_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
80 | `set_by` varchar(128) DEFAULT NULL,
81 | PRIMARY KEY (`variable`)
82 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8
83 | ```
84 | - variable : 配置选项名称
85 | - value : 配置选项值
86 | - set_time : 该行配置修改的时间
87 | - set_by : 该行配置信息修改者,如果从被安装没有修改过,那么这个数据应该为NULL
88 |
89 | 
90 |
91 | 以上值的会话变量为```@sys.```+表中variable字段,如:
92 | ```
93 | @sys.statement_truncate_len
94 | ```
95 |
96 | 可以
97 | ```
98 | set @sys.statement_truncate_len = 32
99 | ```
100 | 临时改变值,在会话中会一直使用这个值,如果想要恢复使用表的默认值,只需要将这个会话值设置为null
101 | ```
102 | set @sys.statement_truncate_len = null;
103 | ```
104 | #### diagnostics.allow_i_s_tables
105 | 默认为OFF ,如果开启表示允许diagnostics() 存储过程执行扫描information_schema.tables 表,如果表很多,那么可能会很耗性能
106 |
107 | #### diagnostics.include_raw
108 | 默认为OFF,开启将会从metrics 视图输出未加工处理的数据
109 | #### statement_performance_analyzer.limit
110 | 视图在没有加limit限制时,返回的最大行数
111 | #### statement_truncate_len
112 | 通过format_statement()函数返回值的最大长度
113 |
114 | #### debug
115 | 这个表非默认选项还有一个@sys.debug参数,可以手动加入
116 | ```
117 | INSERT INTO sys_config (variable, value) VALUES('debug', 'ON');
118 | UPDATE sys_config SET value = 'OFF' WHERE variable = 'debug';
119 | SET @sys.debug = NULL;
120 | ```
121 |
122 | ### 关于此表有两个触发器
123 | #### sys_config_insert_set_user触发器
124 | 如果加入新行通过insert语句,那么这个触发器会把set_by列设置为当前操作者
125 | #### sys_config_update_set_user触发器
126 | 如果加入新行通过update语句,那么这个触发器会把set_by列设置为当前操作者
127 |
128 | # MYSQL SHOW 命令
129 | | 命令 | 注释 |
130 | | ------------ | ------------ |
131 | | desc [table_name] | 表信息 |
132 | | show columns from [table_name] | 表字段 |
133 | | describe [table_name] | 表信息 |
134 | | show create table [table_name] | 表创建语句 |
135 | | show create database [database_name] | 显示数据库信息 |
136 | | show table status from [database_name] |数据库状态|
137 | | show tables |显示当前数据库中所有表的名称 |
138 | | show tables from [database_name] | 显示当前数据库中所有表的名称(同上) |
139 | | show databases | 显示mysql中所有数据库的名称 |
140 | | show processlist | 显示系统中正在运行的所有进程,也就是当前正在执行的查询。|
141 | | show table status | 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间 |
142 | | show columns from [table_name] from [database_name] | 显示表中列名称 |
143 | | show grants for user_name@localhost | 显示一个用户的权限,显示结果类似于grant 命令 |
144 | | show index from [table_name] |显示表的索引 |
145 | | show status | 显示一些系统特定资源的信息,例如,正在运行的线程数量 |
146 | | show variables |显示系统变量的名称和值 |
147 | | show privileges | 显示服务器所支持的不同权限 |
148 | | show create database [database_name] |显示create database 语句是否能够创建指定的数据库 |
149 | | show create table [table_name] |显示create database 语句是否能够创建指定的数据库 |
150 | | show engies | 显示安装以后可用的存储引擎和默认引擎|
151 | | show innodb status | 显示innoDB存储引擎的状态 |
152 | | show logs | 显示BDB存储引擎的日志 |
153 | | show warnings | 显示最后一个执行的语句所产生的错误、警告和通知 |
154 | | show errors | 只显示最后一个执行语句所产生的错误|
155 |
156 | 整理转载自:
157 | https://blog.csdn.net/xlxxcc/article/details/51754524
158 |
159 | # 致谢
160 | 感谢你看完这篇文章,作者原文的知识点概括的很全面,但是文章的排版与表达比较凌乱,不适宜新手来查阅,所以我就做了重构文章的一件事。希望可以帮到你。
161 |
--------------------------------------------------------------------------------
/我与Jetbrains的这些年.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 本章主要说一下如何去使用Jetbrains的各类工具,并且在上周参加了Jetbrains开发者日的大会,把参会的感受和体验在这里分享给各位。话不多说,我们进入正题。
5 |
6 | # 使用
7 | 想必各位一定使用过Jetbrains的任意一款产品,就算没用过也应该听说过吧。Jetbrains从开始至今总共分为三大模块
8 | 1. Developer Ide (集成开发工具)
9 | 2. Language (Jetbrains的开发语言 Kotlin)
10 | 3. DevOps (任务管理,持续集成,持续部署的一些东西)
11 |
12 | 对于Developer Ide我推荐使用Jetbrains ToolBox,它是管理所有Jetbrains Ide的工具。
13 |
14 | 
15 |
16 | Jetbrains平均一个月做一次小更新,安装了Jetbrains ToolBox就不必再去关心更新的事情了。并且Jetbrains ToolBox 还提供了项目列表的功能,如果这时候你需要打开一个项目,你无需去关心项目是哪种开发语言做的,只需要打开它并选择你需要开启的项目即可。
17 |
18 | 
19 |
20 |
21 | 这里要注意,使用Alfred的同学,Jetbrains ToolBox将Ide安装的目录不在是/Application而是~/Application,要记得加入到可搜索列表中,否则是找不到启动文件的。
22 |
23 |
24 | > 以PhpStrom为例,来讲解一下Jetbrains Ide的一些有趣的配置。
25 |
26 | ## 快速编码
27 | 这在Jetbrains开发者日上范老师讲过的一个技巧,你需要打开
28 |
29 | > Preferences -> Editor -> {General->Postfix Completion || Live Templates}
30 |
31 | General->Postfix Completion 与 Live Templates 都是为了去提供编码速度的设置。首先我们先看下 Live Templates
32 |
33 |
34 | 
35 |
36 | 当你在输入fore时按空格(当然也可以选择回车,Tab键,这是需要设置的)
37 |
38 | 
39 |
40 | 就会出现完整的foreach语句,你可以把他当做填空题去完善这个结构
41 | ```
42 | fore
43 | ---->
44 | foreach ($ITERABLE$ as $VAR_VALUE$) {
45 | $END$
46 | }
47 | ```
48 | 当然还有一些其他的例如 eco -> echo , prof -> 创建一个protected的类方法。Ide默认已经为我们准备了一些常用的简写方法,涵盖了至少你知道的所有语言。当然如果没有你想要的你也可以自行添加新的模板。
49 |
50 |
51 | 
52 |
53 | 真不愧叫 Live Templates,第二个来介绍下General->Postfix Completion,这个对于第一个来说是另外一种编辑,第一种是通过键入初始化命令来生成模板,例如eco -> echo ,第二种则是以对象形式去便捷的去生成模板。下面举个栗子
54 |
55 |
56 | 
57 |
58 | 当你键入 $a.notnull 则生成 if ($a !== null) {}
59 | ```
60 | $a.notnull
61 | ---->
62 | if ($a !== null) {
63 |
64 | }
65 | ```
66 | 在编码的时候不是每次都会想好整个流程结构在开始编码,所以Live Templates 就有一定的局限性了。这时候 General->Postfix Completion 就起到了很大作用。最后我们介绍 File and Code Templates , 在快捷生成编码及后续生成代码是在编码开始时的一些骚操作,为了将编码更快,我们还需要将常用的文件模板加入其中
67 |
68 |
69 | 
70 |
71 | 例如你长期使用Laravel框架去开发应用,在使用laravel写一个功能的时候我们会经历下面几个步骤
72 |
73 | > 声明一个路由 -> 创建一个Controller,Model,Action ... 文件 -> 调用Model操作数据 -> 返回给用户结果
74 |
75 | 如果你使用PhpStrom你大致这样写
76 | 1. 打开路由文件 router+空格 完成一个路由结构的自动生成 (当然你需要提前设置)
77 | 2. 创建一个 Php Controller,Model,Action 文件,文件结构已经设置好
78 | 3. 使用General->Postfix Completion设置好的写法分分钟解决所有操作
79 |
80 | ## Database
81 | PhpStrom内置了Database,面板十分简洁易用。他面板的右上角
82 | 
83 | 有兴趣你可以去试试
84 |
85 | ## Api
86 | 细心的朋友一定知道PhpStrom内置了接口测试工具,他在 Tools -> Http Client -> Test RestFul Web Service 下。
87 |
88 | 
89 |
90 | 临时的测试你不必打开万能的Postman,你完全可以通过ide内置的测试工具去完成api测试。当然还有另外一种更厉害的方式 -> 创建一个 test.http 文件
91 | ```
92 | GET www.baidu.com
93 | ```
94 |
95 | 
96 |
97 | 像上述这样,点击运行你将获得Response
98 | ```
99 | GET http://www.baidu.com
100 |
101 | HTTP/1.1 200 OK
102 | Date: Fri, 23 Nov 2018 05:19:42 GMT
103 | Server: Apache
104 | Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
105 | ETag: "51-47cf7e6ee8400"
106 | Accept-Ranges: bytes
107 | Content-Length: 81
108 | Cache-Control: max-age=86400
109 | Expires: Sat, 24 Nov 2018 05:19:42 GMT
110 | Connection: Keep-Alive
111 | Content-Type: text/html
112 |
113 |
114 |
115 |
116 |
117 |
118 | Response code: 200 (OK); Time: 71ms; Content length: 81 bytes
119 | ```
120 | 当然对比Postman依旧不逊色,既然已经将软件改为编码形式,那在Postman内的功能,我们在.http文件中很方便的就可以实现。可以添加请求参数
121 | ```
122 | GET http://www.baidu.com
123 | Content-Type: application/json
124 | {
125 | "name":"zhangsan"
126 | }
127 |
128 | ```
129 | 每个请求以下一个请求方式前结束。具体请移步官网查看。
130 |
131 | ## 主题
132 | 工欲善其事,必先利其器。上面说的Jetbrains提供的功能只是冰山一角,想要具体的学习如何使用还请移步官方。帅气的UI也是调整开发效率的一部分。看到自己的ide非常漂亮,编码的心情自己好的不得了。我使用的是 Material Theme UI
133 |
134 |
135 | 
136 |
137 | 关于字体的调整可以在 Editor -> Font 下进行
138 |
139 | 
140 |
141 | 当然如果你不仅仅从事PHP相关的开发,好不容易配置出来一个自己满意的IDE,写Go的时候用Goland还要配置,大可不必这样。你可以通过 File -> Export Settings 来导出你的配置
142 |
143 |
144 | 
145 |
146 | 随后在通过 File -> Import Settings 来导入你的配置。这样就OK了。如果你购买了 Jetbrains 全家桶或者任意的Ide,也可以通过File -> Sync Settings To Jetbrains Account 来同步到你的Jetbrains账号,类似于云同步一样。
147 |
148 | # 便捷
149 | 剩下的就是快捷键了,快捷键乃IDE编码之本,什么?不需要? 那你咋不用Word编码去?快捷键我就不总结了,网络上太多了。下面贴出laravel-china一位大神的总结。
150 |
151 | https://laravel-china.org/topics/5420/your-keyboard-shortcuts-please
152 | # 大会
153 | 以上说了一部分大会的内容了。Jetbrains的开发者日也并不是全部商业宣传,至少它做的事情是值得让开发者认可的。PPT无法分享给各位,我也不知道怎么拿到。抱歉!大会主要将了以下几项内容,上午是主会场,下午分俩个会场 Kotlin & Java 和 Web,作为一个PHP程序员,我没得选。
154 |
155 |
156 | ----------
157 | 上午
158 |
159 | 1. 大会开始,说了很多关于他们的语言 Kotlin 的使用方法。
160 | 2. 随后说了Jetbrains内部的工作方式及管理方法
161 | 3. 开始ide -> Kotlin 显示 Kotlin特性
162 |
163 |
164 | ----------
165 | 下午
166 |
167 | 1. 简洁高效的PHP编程指南,推荐了一下测试、调试方法&类库及PhpStrom的使用方法
168 | 2. 在真实世界中进行 Go性能优化 讲的go pprof
169 | 3. 用TDD学习高效开发 开始这大佬写了一通的java测试用例,不咋能看懂。后面说了一些关于程序人生的事情
170 | 4. JetBrains的无痛DevOps解决方案 最后讲了JetBrainsDevOps的一些套件,例如TeamCity (与TravisCi类似) ,Upsource 等等。具体可参考 https://www.jetbrains.com/devops/?fromMenu
171 |
172 | ----------
173 |
174 | 总而言之,总体下来大多都是干货,至少我感觉没有任何商业宣传的性质。
175 |
176 | # 致谢
177 | 到这里本章就结束了,感谢看到这里,不过我还要提醒一句给各位开发者
178 |
179 | **编辑器不要汉化**
180 | **编辑器不要汉化**
181 | **编辑器不要汉化**
182 |
183 | 相信你可以明白。谢谢!
184 |
--------------------------------------------------------------------------------
/日常划水:短信验证码开发实例.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # 前言
5 | > 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes/Blog
6 |
7 | 大家好,我是CrazyCodes,在日常开发中有没有遇到过发送短信验证码的接口需要开发?你是如何处理短信验证码发送的呢?本篇我分享下短信验证码发送的设计。
8 |
9 | # 初学者
10 | 以聚合数据为例,初学者会酱紫做
11 |
12 | 百度
13 |
14 | 
15 |
16 | 找到一串既熟悉又陌生的代码 (咋整也记不住的代码)
17 |
18 | ```
19 | //初始化
20 | $curl = curl_init();
21 | //设置抓取的url
22 | curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
23 | //设置头文件的信息作为数据流输出
24 | curl_setopt($curl, CURLOPT_HEADER, 1);
25 | //设置获取的信息以文件流的形式返回,而不是直接输出。
26 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
27 | //设置post方式提交
28 | curl_setopt($curl, CURLOPT_POST, 1);
29 | //设置post数据
30 | $post_data = array(
31 | "username" => "coder",
32 | "password" => "12345"
33 | );
34 | curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
35 | //执行命令
36 | $data = curl_exec($curl);
37 | //关闭URL请求
38 | curl_close($curl);
39 | //显示获得的数据
40 | print_r($data);
41 | ```
42 | 官方也给出了一段维护性略差的代码
43 | ```
44 | '*****************', //您申请的APPKEY
55 | 'mobile' => '1891351****', //接受短信的用户手机号码
56 | 'tpl_id' => '111', //您申请的短信模板ID,根据实际情况修改
57 | 'tpl_value' =>'#code#=1234company#=聚合数据' //您设置的模板变量,根据实际情况修改
58 | );
59 |
60 | $content = juhecurl($sendUrl,$smsConf,1); //请求发送短信
61 |
62 | if($content){
63 | $result = json_decode($content,true);
64 | $error_code = $result['error_code'];
65 | if($error_code == 0){
66 | //状态为0,说明短信发送成功
67 | echo "短信发送成功,短信ID:".$result['result']['sid'];
68 | }else{
69 | //状态非0,说明失败
70 | $msg = $result['reason'];
71 | echo "短信发送失败(".$error_code."):".$msg;
72 | }
73 | }else{
74 | //返回内容异常,以下可根据业务逻辑自行修改
75 | echo "请求发送短信失败";
76 | }
77 |
78 | /**
79 | * 请求接口返回内容
80 | * @param string $url [请求的URL地址]
81 | * @param string $params [请求的参数]
82 | * @param int $ipost [是否采用POST形式]
83 | * @return string
84 | */
85 | function juhecurl($url,$params=false,$ispost=0){
86 | $httpInfo = array();
87 | $ch = curl_init();
88 | curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 );
89 | curl_setopt( $ch, CURLOPT_USERAGENT , 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22' );
90 | curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 30 );
91 | curl_setopt( $ch, CURLOPT_TIMEOUT , 30);
92 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true );
93 | if( $ispost )
94 | {
95 | curl_setopt( $ch , CURLOPT_POST , true );
96 | curl_setopt( $ch , CURLOPT_POSTFIELDS , $params );
97 | curl_setopt( $ch , CURLOPT_URL , $url );
98 | }
99 | else
100 | {
101 | if($params){
102 | curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params );
103 | }else{
104 | curl_setopt( $ch , CURLOPT_URL , $url);
105 | }
106 | }
107 | $response = curl_exec( $ch );
108 | if ($response === FALSE) {
109 | //echo "cURL Error: " . curl_error($ch);
110 | return false;
111 | }
112 | $httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE );
113 | $httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) );
114 | curl_close( $ch );
115 | return $response;
116 | }
117 | ```
118 | 这样看,“也不是不好,就是想再改改,至于改什么,不知道,就是想再改改”
119 |
120 | # 接口
121 | 那么我就开始自己的表演了。首先看本篇你需要了解
122 | - 设计模式中的策略模式
123 | - 依赖注入
124 | - 接口 (interface)
125 | 否则,你看的会很懵 (大佬请略过)
126 |
127 | 首先我们要写一个interface用于规范发短信这个动作
128 | ```
129 | namespace App\Api\Common;
130 |
131 | interface MessageUseInterface
132 | {
133 | public function insert($mobile, $code);
134 | }
135 | ```
136 | 要求所有发短信的动作都必须继承这个接口,并且实现insert方法。
137 |
138 | # 短信类
139 | 接口设定好,我们需要设定一个父类,既发短信的类,具体实现如下
140 | ```
141 | namespace App\Api\Common;
142 |
143 | use GuzzleHttp\Client;
144 |
145 | class Message
146 | {
147 | /**
148 | * @param $mobile
149 | * @param $tpl
150 | * @param $code
151 | * @param MessageUseInterface $use
152 | * @return mixed
153 | * @throws \GuzzleHttp\Exception\GuzzleException
154 | */
155 | public function send($mobile, $tpl, $code, MessageUseInterface $use)
156 | {
157 | $client = new Client();
158 | $response = $client->request('POST', 'http://v.juhe.cn/sms/send', [
159 | 'form_params' => [
160 | 'mobile' => $mobile,
161 | 'tpl_id' => $tpl,
162 | 'tpl_value' => sprintf('#code#=%s', $code),
163 | 'key' => 'xxxxxxxxxxxx'
164 | ]
165 | ]);
166 |
167 | $use->insert($mobile, $code);
168 |
169 | return json_decode($response->getBody(), true);
170 | }
171 | }
172 | ```
173 | 这个类创建了send方法,参数分别为
174 | - mobile 既接收短信验证码的手机号码
175 | - tpl_id 聚合数据提供的模板编码
176 | - code 发送的验证码
177 | - MessageUseInterface 上面创建的接口interface
178 |
179 | 具体实现则是使用GuzzleHttp去实现POST请求,并按聚合数据规定发送验证码。
180 | ```
181 | $use->insert($mobile, $code);
182 | ```
183 | 则是调用通过MessageUseInterface传进来的实体类
184 |
185 | # 实体类
186 | 之后我们创建实体类,这里以手机号+验证码登录为例
187 | ```
188 | namespace App\Api\Common\Message;
189 |
190 |
191 | use App\Api\Common\MessageUseInterface;
192 | use App\Api\Common\Redis;
193 |
194 | class Login implements MessageUseInterface
195 | {
196 | public function insert($mobile, $code)
197 | {
198 | $redis = Redis::init();
199 | $key = sprintf('login_code:%s', $mobile);
200 | $redis->setex($key, 600, $code);
201 | }
202 | }
203 | ```
204 | Login继承接口MessageUseInterface 并实现insert方法。这里redis设定的规范为
205 | ```
206 | module:手机号 -> value(需要发送的验证码)
207 | ```
208 | 至此,我们的短信验证码发送的例子就结束了。
209 | # 使用
210 | 我们可以下列方式调用,或者使用Laravel的服务提供者
211 | ```
212 | (new Message())->send($tel, 141345, mt_rand(100000, 999999), new Register());
213 | ```
214 | 这样既解决了乱七八糟的各种验证码,还提高了代码的可维护性,如果老板有新的需求,例如,支付验证码什么的,你只需要新建一个Pay的验证码类,即完成支付验证码的功能。
215 |
216 | # 致谢
217 | 上述只是一个简单的例子,实体类并未做更多的延伸,请自行发挥创造力。
218 |
219 | 感谢你看到这里,希望本篇文章可以帮到你。谢谢。
220 |
--------------------------------------------------------------------------------
/PHP/使用GrumPHP来纠正代码“毛病”.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # 前言
5 | > 我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes/Blog
6 |
7 | 嗨,我是CrazyCodes,小时候做错事,长辈有没有训斥过你呢?今天让我们看看PHP的监护者,愤怒的老头- - grumphp
8 |
9 | # GrumPHP
10 | GrumPHP 是通过挂在git hook上的一款PHP代码检测工具,他可以通过编码人员提交git时进行检查,检查通过则提交成功,检查失败则终止提交。
11 |
12 | # 安装
13 | 通过composer直接安装即可,不推荐其他安装方式
14 | ```
15 | composer require --dev phpro/grumphp
16 | ```
17 | 这类工具一定要放在dev内,在生产环境使用毫无意义,所以带参 --dev。
18 | # 配置
19 | 安装结束后会自动在项目根目录建立grumphp.yml,官方给出的demo如下
20 | ```
21 | # grumphp.yml
22 | parameters:
23 | bin_dir: "./vendor/bin"
24 | git_dir: "."
25 | hooks_dir: ~
26 | hooks_preset: local
27 | git_hook_variables:
28 | VAGRANT_HOST_DIR: .
29 | VAGRANT_PROJECT_DIR: /var/www
30 | EXEC_GRUMPHP_COMMAND: exec
31 | stop_on_failure: false
32 | ignore_unstaged_changes: false
33 | hide_circumvention_tip: false
34 | process_async_limit: 10
35 | process_async_wait: 1000
36 | process_timeout: 60
37 | ascii:
38 | failed: grumphp-grumpy.txt
39 | succeeded: grumphp-happy.txt
40 | tasks:
41 | ant: ~
42 | atoum: ~
43 | behat: ~
44 | brunch: ~
45 | clover_coverage: ~
46 | codeception: ~
47 | composer: ~
48 | composer_normalize: ~
49 | composer_require_checker: ~
50 | composer_script: ~
51 | deptrac: ~
52 | doctrine_orm: ~
53 | file_size: ~
54 | gherkin: ~
55 | git_blacklist: ~
56 | git_branch_name: ~
57 | git_commit_message: ~
58 | grunt: ~
59 | gulp: ~
60 | infection: ~
61 | jsonlint: ~
62 | kahlan: ~
63 | make: ~
64 | npm_script: ~
65 | paratest: ~
66 | phan: ~
67 | phing: ~
68 | php7cc: ~
69 | phpcpd: ~
70 | phpcs: ~
71 | phpcsfixer: ~
72 | phpcsfixer2: ~
73 | phplint: ~
74 | phpmd: ~
75 | phpmnd: ~
76 | phpparser: ~
77 | phpspec: ~
78 | phpstan: ~
79 | phpunit: ~
80 | phpunitbridge: ~
81 | phpversion: ~
82 | progpilot: ~
83 | psalm: ~
84 | robo: ~
85 | securitychecker: ~
86 | shell: ~
87 | twigcs: ~
88 | xmllint: ~
89 | yamllint: ~
90 | testsuites: []
91 | extensions: []
92 | ```
93 | ## 参数说明
94 | | 参数名 | 默认值 | 注释 |
95 | | -- | -- | -- |
96 | | bin_dir | ./vendor/bin | 方便找到phpcs这类检测工具的外部命令,一般不需要修改 |
97 | | git_dir | . | 默认git目录,正常都在根目录的吧 |
98 | | hooks_dir | null | 设置钩子文件夹,默认会直接找 resources/hooks |
99 | | tasks | | 用于加载代码检测的库 |
100 |
101 | # 实战
102 | 是不是太多了,忽略上面,咱一步一步看。下面是安装完成后自动生成的配置文件
103 | ```
104 | parameters:
105 | git_dir: .
106 | bin_dir: vendor/bin
107 | tasks: { }
108 | ```
109 | ## 准备
110 | 现在目录是酱紫的
111 |
112 | 
113 |
114 | 1. 首先在github建立一个库,用于测试。https://github.com/CrazyCodes/grumphp_test
115 |
116 | 2. 默认的配置不会检测任何代码。我们为 tasks 加入一个新成员 https://github.com/phpro/grumphp/blob/master/doc/tasks/phpcs.md
117 |
118 | 3. 重新设置配置文件
119 | ## 骚操作
120 | ```
121 | parameters:
122 | git_dir: .
123 | bin_dir: vendor/bin
124 | tasks: {
125 | phpcs : ~
126 | }
127 | ```
128 |
129 | 新建一个文件easy.php,代码如下,多一个分号,老头是一定不会放过你的。
130 | ```
131 | echo '召唤愤怒的老头';;
132 | ```
133 | 老头是酱紫说的,额,其实他是一个红色的
134 | ```
135 | GrumPHP detected a pre-commit command.
136 | GrumPHP is sniffing your code!
137 | Running task 1/1: Phpcs... ✘
138 | ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
139 | ▄▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
140 | ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
141 | ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
142 | ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
143 | ▄███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
144 | █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
145 | ▐█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
146 | ▀█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
147 | ▀▀▓▓▓▓▓▓▓▓▓▓▓▓█▀▀▀▀▀▀▀▀▀▀▀▀▀▀████████████▄
148 | ▄███████ ██████████
149 | ███████▀ ▀▀▀▀▀▄ ▄▀▀▀▀▀ █████ ▀
150 | ▐████ ▐██ ▐██ ████▌
151 | ████▌ ███
152 | ▌██▌ ▄▄ ▄▄ ▐███
153 | ███ ▄▄▄▄▄▄▄▄▄▄▄▄ ▐███
154 | ██▄ ▐███████████████████████████
155 | █▀███████████▀ ▀▀███████████
156 | ██████████▄███████▄███████████
157 | ▐█████████████████████████████
158 | █████████████████████████████
159 | ██ █████████████████████▐██▀
160 | ▀ ▐███████████████████▌ ▐▀
161 | ████▀████████▀▐███
162 | ▀█▌ ▐█████ ██▌
163 | ██▀ ▐▀
164 |
165 | ██████████████████████████████████
166 | █░░░░░░▀█▀░░░░░░▀█░░░░░░▀█▀░░░░░▀█
167 | █░░▐█▌░░█░░░██░░░█░░██░░░█░░░██░░█
168 | █░░▐█▌░░█░░░██░░░█░░██░░░█░░░██░░█
169 | █░░▐█▌░░█░░░██░░░█░░░░░░▄█░░▄▄▄▄▄█
170 | █░░▐█▌░░█░░░██░░░█░░░░████░░░░░░░█
171 | █░░░█░░░█▄░░░░░░▄█░░░░████▄░░░░░▄█
172 | ██████████████████████████████████
173 |
174 | FILE: /Users/crazy/http/app/yield_test/easy.php
175 | ----------------------------------------------------------------------
176 | FOUND 1 ERROR AFFECTING 1 LINE
177 | ----------------------------------------------------------------------
178 | 2 | ERROR | Missing file doc comment
179 | ----------------------------------------------------------------------
180 |
181 | Time: 37ms; Memory: 4MB
182 |
183 | ```
184 | 不仅仅有愤怒的老头,GrumPHP还提示了一些其他内容。
185 | - FILE 检测文件路径
186 | - FOUND 1 ERROR AFFECTING 1 LINE 有几个错误,在哪一行
187 | - 2 | ERROR | Missing file doc comment 错误的问题是什么
188 |
189 | 这次检测是说我没写注释。是不是很有意思?GrumPHP不仅仅就这点能力哦,想深入请自行挖掘。
190 |
191 | - 参数列表 https://github.com/phpro/grumphp/blob/master/doc/parameters.md
192 | - 质量检测包 https://github.com/phpro/grumphp/blob/master/doc/tasks.md
193 |
194 |
195 | # 其他
196 | 有人问,你为什么要这么折磨自己呢?其实像类型GrumPHP代码质量工具,不是仅仅自己拿来玩的,在开发人员略多的技术团队,可以通过使用它来达到代码规范一致,如果每个人代码都不一样,后果不堪设想。
197 |
198 | 
199 |
200 |
201 | # 致谢
202 | 感谢你看到这里,希望本篇文章可以帮到你。
203 |
204 |
--------------------------------------------------------------------------------
/Shop/电商系统设计之商品接口.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 我应该是少数在文章中直接展示接口文档的人。本篇我思考了很久到底要不要解析下商品接口开发的注意点。
5 |
6 | 客户端开发与服务端开发即是天敌也是兄弟。希望本篇文章让你们减少争执,把“爱”给对方。
7 | # 接口设计
8 |
9 | ## 简述
10 | 电商系统设计之中,比较复杂的接口就论商品详情的接口了,响应参数特别多,特别杂。在开发获取商品详情接口时要遵循以下几个原则
11 |
12 | - 返回的JSON嵌套数量要少
13 | - 方便去查询到指定的SKU
14 | - 其他接口相关规范
15 |
16 |
17 | 
18 |
19 | ## 查询SKU
20 | 关于查询SKU,我让我的小伙伴是这样做的,首先拿出规格和属性
21 | ```
22 | "选择颜色": [
23 | {
24 | "name": "银色",
25 | "id": 75
26 | }
27 | ],
28 | "选择版本": [
29 | {
30 | "name": "公开版",
31 | "id": 77
32 | },
33 | {
34 | "name": "【原厂延保版】",
35 | "id": 78
36 | }
37 | ],
38 | "内存": [
39 | {
40 | "name": "64G",
41 | "id": 82
42 | },
43 | {
44 | "name": "256G",
45 | "id": 83
46 | }
47 | ],
48 |
49 | ```
50 | 没错,你没有看错,实际就是将规格作为key,属性作为value。将value[id]取出,进行拼接即可查询到对应的SKU了。
51 | ```
52 | 响应参数[规格名称][属性编码] = 拼接SKU串的必需品
53 | ```
54 | 规格相当于一个分组,属性其实也是拼接SKU的重要组成部分,上述数据为例
55 | ```
56 | 75_77_82 = 银色,公开版,64G
57 | ```
58 |
59 | # 接口文档
60 | ## 请求地址
61 | ``/v1/product/{productId}``
62 | ## 请求类型
63 | `` GET ``
64 | ## 请求参数
65 | | 参数 | 类型 | 默认值 | 说明 |
66 | | -- | -- | -- | -- |
67 | | productId | int | 0 | 商品编码 |
68 |
69 | ## 响应示例
70 | ```
71 | {
72 | "code": 200,
73 | "message": "获取成功",
74 | "data": {
75 | "id": 131,
76 | "name": "Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机",
77 | "price": "8388.00",
78 | "market_price": "8388.00",
79 | "sketch": "IPhone大法好,打九折,打九折,快剁手",
80 | "intro": "这是商品描述",
81 | "keywords":['苹果','iphone'],
82 | "attribute": {
83 | "选择颜色": [
84 | {
85 | "name": "银色",
86 | "id": 75
87 | },
88 | {
89 | "name": "深空灰色",
90 | "id": 76
91 | }
92 | ],
93 | "选择版本": [
94 | {
95 | "name": "公开版",
96 | "id": 77
97 | },
98 | {
99 | "name": "【原厂延保版】",
100 | "id": 78
101 | },
102 | {
103 | "name": "双网通版",
104 | "id": 79
105 | },
106 | {
107 | "name": "无线充套装",
108 | "id": 80
109 | },
110 | {
111 | "name": "Airpods套装",
112 | "id": 81
113 | }
114 | ],
115 | "内存": [
116 | {
117 | "name": "64G",
118 | "id": 82
119 | },
120 | {
121 | "name": "256G",
122 | "id": 83
123 | }
124 | ],
125 | "购买方式": [
126 | {
127 | "name": "官方标配",
128 | "id": 84
129 | },
130 | {
131 | "name": "移动优惠购",
132 | "id": 85
133 | },
134 | {
135 | "name": "电信优惠购",
136 | "id": 86
137 | },
138 | {
139 | "name": "联通优惠购",
140 | "id": 87
141 | }
142 | ]
143 | },
144 | "album": [
145 | {
146 | "id": 2,
147 | "name": "这是第一张图片",
148 | "url": "http://xxx.com/59ec33eaN6ddb0c54.jpg"
149 | },
150 | {
151 | "id": 3,
152 | "name": "这是第二张图片",
153 | "url": "http://xxx.com/59ec3400Nce4cc116.jpg"
154 | }
155 | ],
156 | "radio": {
157 | "id": 1,
158 | "name": "这是一个视频",
159 | "url": "http://xxx.com/1.mp4"
160 | },
161 | "sku": {
162 | "75_77_82_84": {
163 | "id": 1018,
164 | "name": "选择颜色:银色;选择版本:公开版;内存:64G;购买方式:官方标配;",
165 | "price": "8388.00",
166 | "stock": 83888388
167 | },
168 | "75_77_82_85": {
169 | "id": 1019,
170 | "name": "选择颜色:银色;选择版本:公开版;内存:64G;购买方式:移动优惠购;",
171 | "price": "8388.00",
172 | "stock": 83888388
173 | },
174 | "75_77_82_86": {
175 | "id": 1020,
176 | "name": "选择颜色:银色;选择版本:公开版;内存:64G;购买方式:电信优惠购;",
177 | "price": "8388.00",
178 | "stock": 83888388
179 | },
180 | "75_77_82_87": {
181 | "id": 1021,
182 | "name": "选择颜色:银色;选择版本:公开版;内存:64G;购买方式:联通优惠购;",
183 | "price": "8388.00",
184 | "stock": 83888388
185 | },
186 | "75_77_83_84": {
187 | "id": 1022,
188 | "name": "选择颜色:银色;选择版本:公开版;内存:256G;购买方式:官方标配;",
189 | "price": "8388.00",
190 | "stock": 83888388
191 | },
192 | "75_77_83_85": {
193 | "id": 1023,
194 | "name": "选择颜色:银色;选择版本:公开版;内存:256G;购买方式:移动优惠购;",
195 | "price": "8388.00",
196 | "stock": 83888388
197 | }
198 | }
199 | }
200 | }
201 | ```
202 |
203 | ## 响应参数说明
204 |
205 | ### 核心参数
206 | | 参数 | 类型 | 默认值 | 说明 |
207 | |:----------|:--------------|:-----------|:------------------|
208 | | id | int | 0 | 商品编码 |
209 | | name | string | - | 商品标题 |
210 | | price | double | 00.00 | 商品价格|
211 | | keywords| string| - | 商品关键字 |
212 | | market_price | double | 00.00| 市场价格|
213 | | virtual | int | 0| 虚拟销量|
214 | | sketch | string |-| 商品简述|
215 | | intro | string | - | 商品详情 |
216 |
217 | ### 商品图参数
218 | | 参数 | 类型 | 默认值 | 说明 |
219 | |:----------|:--------------|:-----------|:------------------|
220 | | album[] | array |[] | 商品轮播图 |
221 | | id | int | 0 | 资源编码 |
222 | | name | string | - | 图片名称|
223 | | url | string | - | 资源路径 |
224 |
225 | ### 商品视频参数
226 | > 无视频则返回 []
227 |
228 | | 参数 | 类型 | 默认值 | 说明 |
229 | |:----------|:--------------|:-----------|:------------------|
230 | | radio[] | array | []| 商品视频 |
231 | | id | int |0| 资源编码 |
232 | | name | string | -| 视频名称|
233 | | url | string |- | 资源路径 |
234 |
235 | ### 商品规格/属性参数
236 | | 参数 | 类型 | 默认值 | 说明 |
237 | |:----------|:--------------|:-----------|:------------------|
238 | | attribute | array[] | [] | 商品属性 |
239 | | [(attr_name)] [] | array[] | []| 属性名称 |
240 | | name | string |-| 属性项名称|
241 | | id | int | 0| 属性项编码 |
242 |
243 |
244 | ### 商品SKU参数
245 | | 参数 | 类型 | 默认值 | 说明 |
246 | |:----------|:--------------|:-----------|:------------------|
247 | | sku[] | array[] | [] | 商品sku |
248 | | [(option_id)] [] | array[] | | 商品SKU查询办法为 attribute[(attr_name)]['id'] 拼接 |
249 | | id | int | 0 | sku编码|
250 | | name | string | - | sku 名称 |
251 | | price | double |00.00| 商品价格 |
252 | | stock | int |0 | 商品库存|
253 |
254 |
255 | # 致谢
256 | 字不在多,讲清楚就行,感谢你看到这里,希望本篇文章可以帮助到你,有疑问可以在评论区讨论,谢谢。
--------------------------------------------------------------------------------
/Shop/电商系统设计之用户系统.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | > 电商大伙每天都在用,类似某猫,某狗等。
4 | > 电商系统设计看似复杂又很简单,看似简单又很复杂
5 | > 本章适合初级工程师及中级工程师细看,大佬请随意
6 |
7 | # 前言
8 | 设计以以下为工具讲起
9 | - PHP为开发语言
10 | - 基于Laravel框架
11 | - MySQL为数据存储
12 |
13 |
14 | 电商的可变性与孩子的心情一样,变化极快,所以在设计之处就要想好大部分的功能接入及开发,尽量减少重构次数。对老板来说节约成本,对程序员来说“珍惜生命”
15 |
16 | # 数据表
17 | 前期业务简单时,我们可以将数据表设计为下列的样子
18 |
19 |
20 | | TableName| Comments |
21 | | :-- | :-- |
22 | | member | 用户表 |
23 | | member_address | 收货地址表 |
24 | | member_card | 银行卡表 |
25 | | member_cart | 购物车表 |
26 | | member_cart_item | 购物车商品表 |
27 | | member_collect_product | 商品收藏表 |
28 | | member_collect_supplier | 店铺收藏表 |
29 | | member_data | 用户信息表 |
30 | | member_query_history | 用户搜索历史表 |
31 | | member_wallet | 用户账户表 |
32 | | member_withdrawal | 用户提现表 |
33 |
34 | ## 用户表
35 | 考虑到多种登录方式,应在数据表中涉及到微信的openid,unionid,支付宝、QQ的用户token等,这些要在前期就涉及进去,因后期用户量大了之后加一个字段简直是噩梦,用户状态status也必不可少,比较人也是分好坏,其次就是创建时间,登录时间等,用户表与用户信息表绝逼是绑定关系,这就不多言了。
36 | ```
37 | CREATE TABLE `member` (
38 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
39 | `tel` bigint(20) DEFAULT NULL COMMENT '手机号码',
40 | `password` varchar(555) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '登录密码',
41 | `wx_token` varchar(125) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '微信TOKEN',
42 | `im_token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户融云token',
43 | `open_id` varchar(125) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
44 | `status` enum('1','-1') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1' COMMENT '账号状态',
45 | `created_at` timestamp NULL DEFAULT NULL,
46 | `updated_at` timestamp NULL DEFAULT NULL,
47 | PRIMARY KEY (`id`),
48 | UNIQUE KEY `member_tel_unique` (`tel`),
49 | UNIQUE KEY `member_wx_token_unique` (`wx_token`)
50 | ) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
51 | ```
52 | 
53 |
54 | ## 收货地址表
55 | 收货地址与用户是一一相对的,在设计上增加需要的字段即可,例如 收货人、收货人手机号、城市、详细地址等
56 | ```
57 | CREATE TABLE `member_address` (
58 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
59 | `member_id` int(11) NOT NULL COMMENT '用户编号',
60 | `nick_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货人姓名',
61 | `tel` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号码',
62 | `prov` int(11) DEFAULT NULL COMMENT '省',
63 | `city` int(11) NOT NULL COMMENT '市',
64 | `area` int(11) DEFAULT NULL COMMENT '区',
65 | `address` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '街道地址',
66 | `number` int(11) NOT NULL COMMENT '邮政编码',
67 | `default` enum('0','1') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '默认收货地址 1=>默认',
68 | `deleted_at` timestamp NULL DEFAULT NULL,
69 | `created_at` timestamp NULL DEFAULT NULL,
70 | `updated_at` timestamp NULL DEFAULT NULL,
71 | PRIMARY KEY (`id`)
72 | ) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
73 | ```
74 | 
75 |
76 | ## 银行卡表
77 | 用于用户提现的业务等,大致将银行卡所需的信息记录即可,例如持卡人、卡号、归属银行等
78 | ```
79 | CREATE TABLE `member_card` (
80 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
81 | `member_id` int(11) NOT NULL COMMENT '用户编码',
82 | `card_name` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '持卡人姓名',
83 | `card_number` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '银行卡号',
84 | `created_at` timestamp NULL DEFAULT NULL,
85 | `updated_at` timestamp NULL DEFAULT NULL,
86 | PRIMARY KEY (`id`),
87 | UNIQUE KEY `member_card_card_number_unique` (`card_number`)
88 | ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
89 | ```
90 | 
91 |
92 | ## 购物车表
93 | 为何单独建这个表,也是又一定原因的,正常只需要member_cart_item表即可,根据实际下线的业务场景,正常购物到超市需要拿一个购物车,但这个购物车并非属于你,你使用之后,需要归还,他人可继续使用,将购物车公开化,并不是将购物车商品公开化。业务场景比较窄,例如京东到家和京东商城一样(我只是举例,并不清楚他们怎么做的),购物车不通用,那如何区分呢,是应该在购物车上区分还是在购物车商品上区分?我想你已经清楚了。
94 | ```
95 | CREATE TABLE `member_cart` (
96 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
97 | `member_id` int(11) NOT NULL COMMENT '用户编码',
98 | `created_at` timestamp NULL DEFAULT NULL,
99 | `updated_at` timestamp NULL DEFAULT NULL,
100 | PRIMARY KEY (`id`),
101 | UNIQUE KEY `member_cart_member_id_unique` (`member_id`),
102 | KEY `member_cart_member_id_index` (`member_id`)
103 | ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
104 | ```
105 | ## 购物车商品表
106 | 这块需要提的一点是 [并不是所有表的设计都是互相绑定,互相依赖的],就例如购物车商品表,不仅仅将商品编码存储在内,还要将商品价格,商品的简介以及商品的规格(既SKU)存储,不能因卖家下架商品,而查询不到商品的存在,比较一切以用户为主,用户是上帝的原则,不能让商品悄悄的就消失了吧。所以在做购物车商品表查询时,切记不要使用join或者表关联查询
107 | ```
108 | CREATE TABLE `member_cart_item` (
109 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
110 | `cart_id` int(11) NOT NULL COMMENT '购物车编码',
111 | `product_desc` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品sku信息',
112 | `product_img` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品快照',
113 | `product_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品名称',
114 | `price` decimal(8,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
115 | `product_id` int(11) NOT NULL COMMENT '商品编码',
116 | `supplier_id` int(11) NOT NULL COMMENT '店铺编码',
117 | `sku_id` int(11) NOT NULL COMMENT '商品sku编码',
118 | `number` int(11) NOT NULL DEFAULT '1' COMMENT '商品数量',
119 | `created_at` timestamp NULL DEFAULT NULL,
120 | `updated_at` timestamp NULL DEFAULT NULL,
121 | PRIMARY KEY (`id`),
122 | KEY `member_cart_item_cart_id_product_id_supplier_id_index` (`cart_id`,`product_id`,`supplier_id`)
123 | ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
124 | ```
125 | ## 用户搜索历史表
126 | 用户搜索的记录是一定要有的,为了未来的数据分析,智能推荐做准备,毕竟现在是信息共享的时代嘛~
127 | ```
128 | CREATE TABLE `member_query_history` (
129 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
130 | `member_id` int(11) NOT NULL COMMENT '用户编码',
131 | `keyword` varchar(125) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关键字',
132 | `created_at` timestamp NULL DEFAULT NULL,
133 | `updated_at` timestamp NULL DEFAULT NULL,
134 | PRIMARY KEY (`id`)
135 | ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
136 | ```
137 | # 数据记录
138 | 有很多场景,都要将标题呀,内容呀直接存储,类似与收藏的店铺和商品,无论卖家怎么做,用户购物车,订单不能动,这是基准。
139 |
140 | # 致谢
141 | 感谢你们看到这里,下一篇我会讲一下关于电商系统的商品设计的部分。有什么问题可以评论区提问。谢谢
--------------------------------------------------------------------------------
/PHP/来!狂撸一款PHP现代化框架 (路由的设计).md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 前言
4 | 上一篇的标题改了一下,以一、二、三为章节对读者来说是种困扰,现在的标题是依照项目进度来编写的。上篇文章地址为 https://segmentfault.com/a/1190000017278828
5 |
6 | 这一系列文章并不准备写太多章节,大概规划的只有4~5章左右,具体实现代码还请移步Github
7 | https://github.com/CrazyCodes/z_framework
8 |
9 | 本章详细讲解一下Route(路由的实现),Come on Up Image
10 |
11 |
12 | 
13 |
14 | 上图大概说明了实现路由要经过两个步骤
15 | - 将所有路由信息存储到超全局变量中
16 | - 用户请求时从全局变量中查找路由映射的服务脚本并实例化
17 |
18 | OK,大概流程就是酱紫,下面开始“撸”
19 |
20 | # 目录
21 | 路由的代码暂分为以下几个文件(这并不是确定的,详细可查看Github)
22 |
23 | | 文件名 | 注释 |
24 | | -- | -- |
25 | | Route | 转发文件:为实现 Route::get 效果 |
26 | | RouteCollection | 路由信息处理存储 |
27 | | RouteInterface | 无需解释 |
28 | | RouteModel | 路由模型,将每个路由信息以结构体方式存储到$_SERVER |
29 | | Router | 路由的核心类 |
30 |
31 | 莫急,我们一个一个文件来看。先从RouteInterface开始
32 |
33 | # RouteInterface
34 | 参照RESTful规定设定接口方法分别为 GET、POST、PATCH、PUT、DELETE、OPTIONS,当然Laravel也是规范了以上标准请求。
35 |
36 | GitHub : https://github.com/CrazyCodes/z_framework/blob/master/src/Zero/ZeroInterface.php
37 | ```
38 | interface RouteInterface
39 | {
40 | /**
41 | * @param $uri
42 | * @param null $action
43 | *
44 | * @return mixed
45 | */
46 | public function get($uri, $action = null);
47 |
48 | /**
49 | * @param $uri
50 | * @param null $action
51 | *
52 | * @return mixed
53 | */
54 | public function post($uri, $action = null);
55 |
56 | /**
57 | * @param $uri
58 | * @param null $action
59 | *
60 | * @return mixed
61 | */
62 | public function patch($uri, $action = null);
63 |
64 | /**
65 | * @param $uri
66 | * @param null $action
67 | *
68 | * @return mixed
69 | */
70 | public function put($uri, $action = null);
71 |
72 | /**
73 | * @param $uri
74 | * @param null $action
75 | *
76 | * @return mixed
77 | */
78 | public function delete($uri, $action = null);
79 |
80 | /**
81 | * @param $uri
82 | * @param null $action
83 | *
84 | * @return mixed
85 | */
86 | public function options($uri, $action = null);
87 | }
88 | ```
89 | # Router
90 | 先写一个栗子
91 | ```
92 | public function get($uri, $action = null)
93 | {
94 | return $this->addRoute("GET", $uri, $action);
95 | }
96 | ```
97 | 用户调用下方代码会指向上述方法,方法既调用addRoute方法将路由信息存储到$_SERVER中
98 | ```
99 | Route::get('/','Controller')
100 | ```
101 | 以下为addRoute部分的代码
102 | ```
103 | public function addRoute($methods, $uri, $action)
104 | {
105 | // 这里判断请求方式是否合规,既是否存在 GET、POST、PATCH、PUT、DELETE、OPTIONS其中之一
106 | if ($this->verify($methods) == false) {
107 | return false;
108 | }
109 |
110 | // 之后我们去往RouteCollection路由信息的处理类中
111 | return $this->routes->add($uri, $this->createRoute($methods, $action));
112 | }
113 | ```
114 |
115 | # RouteCollection
116 | 最终达到 add 方法,将路由信息存储到$_SERVER中
117 | ```
118 | public function add($uri, RouteModel $model)
119 | {
120 | if (empty($_SERVER["routes"][$uri])) {
121 | $_SERVER["routes"][$uri] = $model;
122 | }
123 | }
124 | ```
125 | 第二个参数RouteModel开始我们说过这是路由模型,将每个路由以结构体的方式存储到变量中,存储后的结果
126 | ```
127 | 'routes' =>
128 | array(6) {
129 | 'test/get' =>
130 | class Zero\Routing\RouteModel#13 (2) {
131 | public $method =>
132 | string(3) "GET"
133 | public $action =>
134 | string(19) "testController@test"
135 | }
136 | 'test/post' =>
137 | class Zero\Routing\RouteModel#14 (2) {
138 | public $method =>
139 | string(4) "POST"
140 | public $action =>
141 | string(19) "testController@test"
142 | }
143 | 'test/put' =>
144 | class Zero\Routing\RouteModel#15 (2) {
145 | public $method =>
146 | string(3) "PUT"
147 | public $action =>
148 | string(18) "testController@put"
149 | }
150 | 'test/del' =>
151 | class Zero\Routing\RouteModel#16 (2) {
152 | public $method =>
153 | string(6) "DELETE"
154 | public $action =>
155 | string(18) "testController@del"
156 | }
157 | 'test/patch' =>
158 | class Zero\Routing\RouteModel#17 (2) {
159 | public $method =>
160 | string(5) "PATCH"
161 | public $action =>
162 | string(20) "testController@patch"
163 | }
164 | 'test/opt' =>
165 | class Zero\Routing\RouteModel#18 (2) {
166 | public $method =>
167 | string(7) "OPTIONS"
168 | public $action =>
169 | string(18) "testController@opt"
170 | }
171 | }
172 | ```
173 | # Route
174 | 最后通过__callStatic将代码重定向到核心类中
175 | ```
176 | public static function __callStatic($name, $arguments)
177 | {
178 | $router = new Router;
179 |
180 | return $router->{$name}($arguments[0], $arguments[1]);
181 | }
182 | ```
183 |
184 | 上述套路部分是Laravel的设计思想,通过这款简单的框架可对Laravel核心设计有丁点的理解。
185 | # 测试
186 | 测试上次做的有点糙,从本章到系列结束,我们都以PHPunit来测试。
187 | ```
188 | /**
189 | * @content tests all methods storage -> $_SERVER["routes"]
190 | */
191 | public function testAllMethodsStorage()
192 | {
193 | $this->routes->get($methodGet = "test/get", "testController@test");
194 | $this->assertArrayHasKey($methodGet, $_SERVER[$this->methodsDataKey]);
195 |
196 | $this->routes->post($methodPost = "test/post", "testController@test");
197 | $this->assertArrayHasKey($methodPost, $_SERVER[$this->methodsDataKey]);
198 |
199 | $this->routes->put($methodPut = "test/put", "testController@put");
200 | $this->assertArrayHasKey($methodPut, $_SERVER[$this->methodsDataKey]);
201 |
202 | $this->routes->delete($methodDel = "test/del", "testController@del");
203 | $this->assertArrayHasKey($methodDel, $_SERVER[$this->methodsDataKey]);
204 |
205 | $this->routes->patch($methodPatch = "test/patch", "testController@patch");
206 | $this->assertArrayHasKey($methodPatch, $_SERVER[$this->methodsDataKey]);
207 |
208 | $this->routes->options($methodOpt = "test/opt", "testController@opt");
209 | $this->assertArrayHasKey($methodOpt, $_SERVER[$this->methodsDataKey]);
210 | }
211 | ```
212 | 上述贴出部分代码,以过程化的方法去测试。查看存储是否符合预期。
213 |
214 | ```
215 | /**
216 | * @content RouteModel Success
217 | */
218 | public function testCreateRoute()
219 | {
220 | $response = $this->routes->createRoute("GET", "TestController@Get");
221 |
222 | $this->assertInstanceOf(RouteModel::class, $response);
223 | }
224 | ```
225 | 包括测试对路由创建后是否为RouteModel的实现。具体可查看Github
226 | https://github.com/CrazyCodes/z_framework/tree/master/tests/Routing
227 | # 致谢
228 | 上述已完成了路由的基本设计,下一章将讲解从启动到请求路由映射到服务脚本的过程。
229 |
230 | 希望本章可以帮到你,谢谢。
231 |
--------------------------------------------------------------------------------