└── 博客文章 ├── Head-First-Design-Patterns.md ├── Linux-Shell-基础之变量.md ├── Linux-Shell-基础之条件判断语句.md ├── Linux-Shell-基础之正则表达式.md ├── Linux-Shell-基础之运算符.md ├── Linux-Shell-实战.md ├── Linux-命令基础.md ├── Linux-定时任务.md ├── Linux-服务管理.md ├── Linux-权限管理.md ├── Linux-系统管理.md ├── Linux-网络基础.md ├── Linux-软件安装.md ├── LuisEdware-版-PHP-编程规范(遵循-PSR-1-PSR-2).md ├── MySQL-5-7-版本新特性.md ├── MySQL-性能优化.md ├── PHP Web Application 运行流程、概念术语、机制原理和代码实践.md ├── PHP-Codebook-一.md ├── PHP-Codebook-九.md ├── PHP-Codebook-二.md ├── PHP-Codebook-六.md ├── PHP-Codebook-四.md ├── Swagger-PHP-基础使用.md ├── Yii-框架之基础篇.md ├── Yii-框架之安全篇.md ├── Yii-框架之工具篇.md ├── Yii-框架之扩展篇.md ├── Yii-框架之高效篇.md ├── Yii2-Working-with-Databases.md ├── 「Tips」-htaccess-配置-HTTP-Header.md ├── 「Tips」PHP-7-1-命名空间的新特性和使用方法.md ├── 「Tips」Yii2-前端请求的参数字段映射数据模型的属性.md ├── 「复习」使用阿里云从零开始部署-Laravel.md ├── 与-MySQL-的零距离接触(一).md ├── 与-MySQL-的零距离接触(二).md ├── 使用-Laravel-与-Vue-js-进行微信开发(三).md ├── 使用-Laravel-与-Vue-js-进行微信开发(二).md ├── 使用-Laravel-与-Vue.js-进行微信开发(一).md ├── 使用-Laravel-基于-SMTP-驱动实现发送邮件.md ├── 使用-Laravel-编写自定义命令.md ├── 使用-PHPStorm-与-Xdebug-调试-Laravel-一.md ├── 使用-PHPStorm-与-Xdebug-调试-Laravel-二.md ├── 使用-PHPStorm-的-Live-Templates-封装常用代码.md ├── 使用-Sublime-Text-3-开发-PHP-的总结.md ├── 使用-Yii2-构建-RESTFul-API.md ├── 使用Laravel构建内容管理框架(一).md ├── 使用Laravel构建内容管理框架(七).md ├── 使用Laravel构建内容管理框架(三).md ├── 使用Laravel构建内容管理框架(九).md ├── 使用Laravel构建内容管理框架(二).md ├── 使用Laravel构建内容管理框架(五).md ├── 使用Laravel构建内容管理框架(八).md ├── 使用Laravel构建内容管理框架(六).md ├── 使用Laravel构建内容管理框架(四).md ├── 使用阿里云从零开始部署-Laravel.md ├── 关于-PHP-函数-array-filter-的一些想法.md ├── 关于-PHP-函数-array-map-的一些想法.md ├── 关于-PHP-函数-array-walk-的一些想法.md ├── 在-Yii-2-中使用-CodeCeption.md ├── 复习使用-MySQL(一).md ├── 复习使用-MySQL(三).md ├── 复习使用-MySQL(二).md ├── 学习-Javascript-之变量.md ├── 学习-Javascript-之类型.md ├── 学习-UML(一).md ├── 学习-UML(二).md ├── 打造扛得住的-MySQL-数据库架构.md └── 查看-Eloquent-模型关联时执行的-SQL-语句.md /博客文章/Head-First-Design-Patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Head First Design Patterns 3 | date: 2017-03-20 10:46:53 4 | tags: Learning 5 | category: Design Patterns 6 | --- 7 | 8 | 如何定义模式 9 | 10 | 模式是某种情景下,针对某问题的某种解决方案。 11 | 12 | - 情景 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 | -------------------------------------------------------------------------------- /博客文章/Linux-Shell-基础之变量.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux Shell 基础之变量 3 | date: 2016-10-04 10:50:53 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | ## Shell 变量概述 9 | 10 | Shell 与其他语言的对比 11 | 12 | - PHP 和 JAVA 主要实现功能 13 | - Shell 简化管理操作 14 | 15 | 什么是变量 16 | 17 | - 变量是计算机内存的单元,其中存放的值可以改变 18 | - 变量让你能够把程序中准备使用的每一段数据都赋给一个简短、易于记忆的名字,因此它们十分有用 19 | 20 | 变量命名规则 21 | 22 | - 变量名必须以字母或下划线开头,名字中间只能由字母、数字和下划线组成 23 | - 变量名的长度不得超过 255 个字符 24 | - 变量名在有效的范围内必须是唯一的 25 | - 在 Bash 中,变量的默认类型都是字符串型 26 | 27 | 变量存储类型 28 | 29 | - 整型 30 | - 浮点型 31 | - 日期型 32 | - 字符串型 33 | 34 | 变量定义分类 35 | 36 | - 用户自定义变量:用户自定义的变量 37 | - 环境变量:这种变量中主要保存的是和系统操作环境相关的数据。变量可以自定义,但是对系统生效的环境变量名和变量作用是固定的。 38 | - 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的。变量名不能自定义,变量作用固定。 39 | - 预定义变量:是 Bash 中已经定义好的变量,变量名不能自定义,变量作用也是固定的。 40 | 41 | ## Shell 变量与变量分类 42 | 43 | - 用户自定义变量 44 | - 变量定义 45 | - `变量名=变量值` 46 | - `x=5` 47 | - `name="Luis"` 48 | - 变量赋值时候,变量名与变量值之间不能有空格 49 | - 变量调用 50 | - `echo $x` 51 | - `echo $name` 52 | - 变量叠加 53 | - x=123 54 | - x="$x"456 55 | - x=${x}789 56 | - 变量查看 57 | - set 58 | - \-u 如果设定此选项,调用未声明变量时会报错(默认无任何显示) 59 | - 变量删除 60 | - `unset 变量名` 61 | - 环境变量 62 | - 用户自定义环境变量 63 | - 设置环境变量 64 | - `export 变量名=变量值` 65 | - `变量名=变量值;export 变量名` 66 | - 查看环境变量 67 | - `set` 查看所有变量 68 | - `env` 查看环境变量 69 | - 删除环境变量 70 | - `unset 变量名` 71 | - 系统常用的环境变量 72 | - HOSTNAME 主机名 73 | - SHELL 当前的 Shell 74 | - TERM 终端环境 75 | - HISTSIZE 历史命令条数 76 | - SSH_CLIENT 当前操作环境是用 ssh 连接的,这里记录客户端 IP 77 | - SSH_TTY ssh 连接的终端时 pts/1 78 | - USER 当前登录的用户 79 | - PATH 系统查找命令的路径 80 | - `echo $PATH` 查看 PATH 环境变量 81 | - `PATH="$PATH":/root/sh` 增加 PATH 变量的值 82 | - PS1 变量 命令提示符设置 83 | - \\d 显示日期,格式为"星期 月 日" 84 | - \\H 显示完整的主机名,如默认主机名 "localhost.localdomain" 85 | - \\t 显示 24 小时制时间,格式为 "HH:MM:SS" 86 | - \\A 显示 24 小时制时间,格式为 "HH:MM" 87 | - \\u 显示当前用户名 88 | - \\w 显示当前所在目录的完整名称 89 | - \\W 显示当前所在目录的最后一个目录 90 | - \\$ 提示符。如果是 root 用户会显示提示符为 "#",如果是普通用户会显示提示符为 "$" 91 | - 例子 `PS1='[\u@\A \w]\$ '` 92 | - 语系变量 93 | - `locale` 查询当前系统语系(`echo $LANG`) 94 | - LANG 定义系统主语系的变量 95 | - LC_ALL 定义整体语系的变量 96 | - `locale -a | more` 查看 Linux 支持的所有语系 97 | - `cat /etc/sysconfig/i18n` 查询系统默认语系(下次开机语系变量) 98 | - 位置参数变量 99 | 100 | 位置参数变量|作用 101 | ---|--- 102 | $n|n 为数字,$0 代表命令本身,$1-$9 代表第一到第九个参数,十以上的参数需要用大括号包含,如 ${10} 103 | $\*|这个变量代表命令行中所有的参数,$\* 把所有的参数看成一个整体 104 | $@|这个变量代表命令行中所有的参数,不过 $@ 把每个参数区分对待 105 | $#|这个变量代表命令行中所有参数的个数 106 | 107 | - 预定义变量 108 | 109 | 预定义变量|作用 110 | ---|--- 111 | $? | 最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己决定,则证明上一个命令执行不正确) 112 | $$ | 当前进程的进程号 113 | $! | 后台运行的最后一个进程的进程号 114 | 115 | - 接收键盘输入 116 | - `read [选项] [变量名]` 117 | - \-p 提示信息:在等待 read 输入时,输出提示信息 118 | - \-t 秒数:read 命令会一直等待用户输入,使用此选项可以指定等待时间 119 | - \-n 字符数:read 命令只接受指定的字符数,就会执行 120 | - \-s 隐藏输入的数据,适用于机密信息的输入 121 | 122 | ## Shell 变量例子 123 | 124 | 位置参数例子 1 125 | 126 | ```shell 127 | #!/bin/bash 128 | num1=$1 129 | num2=$2 130 | 131 | # 变量 sum 的和是 num1 加 num2 132 | sum=$(($num1+$num2)) 133 | 134 | # 打印变量 sum 的值 135 | echo $sum 136 | ``` 137 | 138 | 位置参数例子 2 139 | ```shell 140 | #!/bin/bash 141 | 142 | # 使用 $# 代表所有参数的个数 143 | echo "A total of $# parameters" 144 | 145 | # 使用 $* 代表所有的参数 146 | echo "The parameters is:$*" 147 | 148 | # 使用 $@ 也代表所有参数 149 | echo "The parameters is:$@" 150 | ``` 151 | 152 | 位置参数例子 3($* 与 $@ 的区别) 153 | ```shell 154 | #!/bin/bash 155 | 156 | for i in "$*" 157 | # $* 中的所有参数看成是一个整体,所以这个 for 循环只会循环一次 158 | do 159 | echo "The parameters is: $i" 160 | done 161 | 162 | for y in "$@" 163 | # $@ 中的每个参数都看成是独立的,所以 "$@" 中有几个参数,就会循环几次 164 | do 165 | echo "Parameter:$y" 166 | done 167 | ``` 168 | 169 | 接收键盘输入例子 1 170 | ```shell 171 | #!/bin/bash 172 | 173 | read -p "Please input your name: " -t 30 name 174 | echo $name 175 | 176 | read -p "Please input your passwd: " -s passed 177 | echo -e "\n" 178 | echo $passwd 179 | 180 | read -p "Please input your sex [M/F]: " -n 1 sex 181 | echo -e "\n" 182 | echo $sex 183 | ``` 184 | 185 | -------------------------------------------------------------------------------- /博客文章/Linux-Shell-基础之条件判断语句.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux Shell 基础之条件判断语句 3 | date: 2016-10-04 10:56:05 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## Shell 条件判断式语句 10 | 11 | ### 按文件类型判断 12 | 13 | 测试选项|作用 14 | ---|--- 15 | \-b 文件 | 判断该文件是否存在,并且是否为块设备文件 16 | \-c 文件 | 判断该文件是否存在,并且是否为字符设备文件 17 | \-d 文件 | 判断该文件是否存在,并且是否为目录文件 18 | \-e 文件 | 判断该文件是否存在(存在为真) 19 | \-f 文件 | 判断该文件是否存在,并且是否为普通文件 20 | \-L 文件 | 判断该文件是否存在,并且是否为符号链接文件 21 | \-p 文件 | 判断该文件是否存在,并且是否为管道文件 22 | \-s 文件 | 判断该文件是否存在,并且是否为非空 23 | \-S 文件 | 判断该文件是否存在,并且是否为套接字文件 24 | 25 | 两种判断格式 26 | 27 | - `test -e /root/install.log` 28 | - `[ -e /root/install.log ]` 29 | - `[ -e /root/install.log ] && echo yes || echo no` 30 | 31 | ### 按文件权限判断 32 | 33 | 测试选项|作用 34 | ---|--- 35 | \-r 文件 | 判断该文件是否存在,并且是否该文件拥有读权限 36 | \-w 文件 | 判断该文件是否存在,并且是否该文件拥有写权限 37 | \-x 文件 | 判断该文件是否存在,并且是否该文件拥有执行权限 38 | \-u 文件 | 判断该文件是否存在,并且是否该文件拥有 SUID 权限 39 | \-g 文件 | 判断该文件是否存在,并且是否该文件拥有 SGID 权限 40 | \-k 文件 | 判断该文件是否存在,并且是否该文件拥有 SBit 权限 41 | 42 | ### 两个文件之间的比较 43 | 44 | 测试选项|作用 45 | ---|--- 46 | 文件1 -nt 文件2 | 判断文件 1 的修改时间是否比文件 2 的新 47 | 文件1 -ot 文件2 | 判断文件 1 的修改时间是否比文件 2 的旧 48 | 文件1 -ef 文件2 | 判断文件 1 是否和文件 2 的 Inode 号一致,可以理解为两个文件是否为同一个文件,这个判断用于硬链接判断是很好的方法 49 | 50 | 例子 51 | ```shell 52 | # 创建一个硬链接 53 | ln /root/student.txt/tmp/stu.txt 54 | 55 | # 测试 56 | [/root/student.txt -ef /tmp/stu.txt] && echo "yes" || echo "no" 57 | ``` 58 | 59 | ### 两个整数之间的比较 60 | 61 | 测试选项|作用 62 | ---|--- 63 | 整数1 -eq 整数2 | 判断整数 1 是否和整数 2 相等 64 | 整数1 -ne 整数2 | 判断整数 1 是否和整数 2 不相等 65 | 整数1 -gt 整数2 | 判断整数 1 是否大于整数 2 66 | 整数1 -lt 整数2 | 判断整数 1 是否小于整数 2 67 | 整数1 -ge 整数2 | 判断整数 1 是否大于等于整数 2 68 | 整数1 -le 整数2 | 判断整数 1 是否小于等于整数 2 69 | 70 | 例子 71 | 72 | ```shell 73 | [ 23 -ge 22 ] && echo "yes" || echo "no" 74 | [ 23 -le 22 ] && echo "yes" || echo "no" 75 | ``` 76 | 77 | ### 字符串的判断 78 | 79 | 测试选项|作用 80 | ---|--- 81 | \-z | 判断字符串是否为空 82 | \-n | 判断字符串是否为非空 83 | 字符串1==字符串2 | 判断字符串 1 是否和字符串 2 相等 84 | 字符串1!=字符串2 | 判断字符串 1 是否和字符串 2 不相等 85 | 86 | 87 | 例子 88 | ```shell 89 | # 给 name 变量赋值 90 | name=fengj 91 | 92 | # 判断 name 变量是否为空 93 | [ -z "$name" ] && echo "yes" || echo "no" 94 | 95 | # 给变量 aa 和变量 bb 赋值 96 | aa=11 97 | bb=22 98 | 99 | # 判断两个变量的值是否相等 100 | [ "$aa" == "$bb" ] && echo yes || echo no 101 | ``` 102 | 103 | ### 多重条件判断 104 | 105 | 测试选项|作用 106 | ---|--- 107 | 判断1 -a 判断2 | 逻辑与,判断 1 和判断 2 都成立,最终的结果才为真 108 | 判断1 -o 判断2 | 逻辑或,判断 1 和判断 2 有一个成立,最终的结果就为真 109 | ! 判断 | 逻辑非,使原始的判断式取反 110 | 111 | 例子 112 | ```shell 113 | # 给变量 aa 赋值 114 | aa=11 115 | 116 | # 判断变量 aa 是否有值,同时判断变量 aa 是否大于 23 117 | [ -n "$aa" -a "$aa" -gt 23 ] && echo "yes" || echo "no" 118 | ``` 119 | 120 | ## Shell 单分支if语句 121 | 122 | ### 概述 123 | 124 | 如何 "背" 程序 125 | 126 | 1. 抄写目标程序并能正确运行 127 | 1. 为目标程序补全注释 128 | 1. 删掉注释,为代码重新加注释 129 | 1. 删除代码,看注释写代码 130 | 1. 删除注释和代码,从头开始写 131 | 132 | ### 单分支 if 语句 133 | 134 | 语法支持 135 | ```Shell 136 | if [ 条件判断式 ];then 137 | 程序 138 | fi 139 | 140 | if [ 条件判断式 ] 141 | then 142 | 程序 143 | fi 144 | ``` 145 | 146 | 例子1:判断登录的用户是否为 root 147 | ```shell 148 | #!/bin/bash 149 | 150 | test=$(env|grep "USER" |cut -d "=" -f2) 151 | 152 | if [ "$test" == root ] 153 | then 154 | echo "Current user is root." 155 | fi 156 | ``` 157 | 158 | ### 单分支 if 语句例子:判断分区使用率 159 | 160 | ```shell 161 | #!/bin/bash 162 | 163 | # 统计根分区使用率,把根分区使用率作为变量值赋予变量 rate 164 | rate=$(df -h | grep "/dev/disk1" | awk '{print $5}' | cut -d "%" -f 1) 165 | 166 | if [ $rate -ge 70 ] 167 | then 168 | echo "Warning! /dev/disk1 is full!!" 169 | fi 170 | ``` 171 | 172 | ## Shell 双分支if语句 173 | 174 | 语法支持 175 | ```shell 176 | if [ 条件判断式 ] 177 | then 178 | 条件成立时,执行的程序 179 | else 180 | 条件不成立时,执行的另一个程序 181 | fi 182 | ``` 183 | 184 | ### 双分支 if 语句例子:判断输入的是否是一个目录 185 | ```shell 186 | #!/bin/bash 187 | 188 | read -t 30 -p "Please input a dir:" dir 189 | 190 | if [ -d "$dir" ] 191 | then 192 | echo "is a Dir" 193 | else 194 | echo "is not a Dir" 195 | fi 196 | ``` 197 | 198 | ### 双分支 if 语句例子:判断Apache服务是否启动 199 | ```shell 200 | #!/bin/bash 201 | 202 | # 列出所有进程,选取 httpd 进程,反选 grep 203 | test=$(ps aux | grep httpd | grep -v grep) 204 | 205 | if [ -n "$test" ] 206 | then 207 | echo "$(date) httpd is ok!" >> /tmp/autostart-acc.log 208 | else 209 | sudo apachectl restart &> /dev/null 210 | echo "$(date) restart httpd !!" >> /tmp/autostart-err.log 211 | fi 212 | ``` 213 | 214 | ## Shell 多分支 if 语句 215 | 216 | 语言支持: 217 | ```shell 218 | if [ 条件判断式 1 ] 219 | then 220 | 条件成立 1 时,执行的程序 221 | elif [ 条件判断式 2 ] 222 | then 223 | 条件成立 2 时,执行的程序 224 | else 225 | 条件不成立时,执行的另一个程序 226 | fi 227 | ``` 228 | 229 | ### 多分支 if 语句例子:判断用户输入的是什么文件 230 | ```shell 231 | #!/bin/bash 232 | # 判断用户输出的是什么文件 233 | 234 | # 接受键盘的输入,并赋予变量file 235 | read -p "Please input a filename:" file 236 | 237 | # 判断变量是否为空 238 | if [ -z "$file" ] 239 | then 240 | echo "Error,Please input a filename" 241 | exit 1 242 | # 判断 file 的值是否存在 243 | elif [ ! -e "$file" ] 244 | then 245 | echo "Your input is not a file!" 246 | exit 2 247 | # 判断 file 的值是否为普通文件 248 | elif [ -f "$file" ] 249 | then 250 | echo "$file is a regulare file!" 251 | # 判断 file 的值是否为目录文件 252 | then 253 | echo "$file is a directory!" 254 | else 255 | echo "$file is an other file!" 256 | ``` 257 | 258 | ## Shell 多分支case语句 259 | 260 | 语法支持 261 | ```shell 262 | case $变量名 in 263 | "值1") 264 | 如果变量的值等于值 1,则执行程序1 265 | ;; 266 | "值2") 267 | 如果变量的值等于值 2,则执行程序2 268 | ;; 269 | *) 270 | 如果变量的值都不是以上的值,则执行此程序 271 | ;; 272 | ``` 273 | -------------------------------------------------------------------------------- /博客文章/Linux-Shell-基础之正则表达式.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux-Shell-基础之正则表达式 3 | date: 2016-09-22 20:56:54 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## 第1章 正则表达式 10 | 11 | ### 1-1 什么是正则表达式 12 | 13 | 正则表达式是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的模式分割、匹配、查找及替换操作。 14 | 15 | ### 1-2 正则表达式与通配符 16 | 17 | - 正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配。grep、awk、sed 等命令可以支持正则表达式(字符串) 18 | - 通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp 这些命令不支持正则表达式,所以只能使用 Shell 自己的通配符来进行匹配了。(文件) 19 | - \* 匹配任意内容 20 | - ? 匹配任意一个内容 21 | - [] 匹配括号中的一个字符 22 | 23 | ### 1-3 基础正则表达式 24 | 25 | 元字符|作用 26 | ---|--- 27 | * | 前一个字符匹配 0 次或任意多次 28 | . | 匹配除了换行符外任意一个字符 29 | ^ | 匹配行首,例如:^hello 会匹配以 hello 开头的行 30 | $ | 匹配行尾,例如:hello& 会匹配以 hello 结尾的行 31 | [] | 匹配中括号中指定的任意一个字符,只匹配一个字符。 32 | [^] | 匹配除中括号的字符以外的任意一个字符 33 | \ | 转义符,用于取消讲特殊符号的含义取消 34 | \\{n\\} | 表示其前面的字符恰好出现 n 次 35 | \\{n,\\} | 表示其前面的字符出现不小于 n 次 36 | \\{n,m\\} | 表示其前面的字符至少出现 n 次,最多出现 m 次 37 | 38 | 39 | ## 第2章 字符截取命令 40 | 41 | - cut 列提取字符串 42 | - `cut [选项] 文件名` 43 | - \-f 列号:提取第几列 44 | - `cut -f 2 student.txt` 截取第二列 45 | - `cut -f 2,4 student.txt` 截取第二、第四列 46 | - \-d 分隔符:按照指定分隔符分割列 47 | - `grep "/bin/bash" /etc/passwd |grep -v "root" | cut -f 1 -d ":"` 48 | - printf 格式化输出命令 49 | - `printf '输出类型输出格式' 输出内容` 50 | - 输出类型 51 | - %ns 输出字符串。n 是数字指代输出几个字符 52 | - %ni 输出整数。n 是数字指代输出几个数字 53 | - %m.nf 输出浮点数。m 和 n 是数字,指代输出的整数位数和小数位数。如 %8.2f 代表共输出 8 位数,其中 2 位是小数,6 位是整数。 54 | - 输出格式 55 | - \\a 输出警告声音 56 | - \\b 输出退格键 57 | - \\f 清除屏幕 58 | - \\n 换行 59 | - \\r 回车 60 | - \\t 水平输出 Tab 键 61 | - \\v 垂直输出 Tab 键 62 | - 在 `awk` 命令的输出中支持 `print` 和 `printf` 命令 63 | - awk 字符截取命令 64 | - `awk '条件1{动作1}条件2{动作2}...' 文件名` 65 | - 条件 66 | - 一般使用关系表达式作为条件 67 | - x>10 判断变量 x 是否大于 10 68 | - x>=10 大于等于 69 | - x<=10 小于等于 70 | - 动作 71 | - 格式化输出 72 | - 流程控制语句 73 | - 例子 74 | - `awk '{printf $2 "\t" $4 "\n"}' student.txt` 75 | - `awk '{print $2 "\t" $4 }' student.txt` 76 | - `df -h | awk '{print $1 "\t" $3}'` 77 | - `akw 'BEGIN{printf "This is a transcript \n"}{printf $2 "\t" $4 "\n"}' student.txt` 78 | - `cat /etc/passwd |grep "/bin/bash" | awk 'BEGIN{FS=":"}{printf $1 "\t" $3 "\n"}'` 79 | - sed 字符替换命令 80 | - sed 是一个几乎包括在所有 UNIX 平台(包括 Linux)的轻量级编辑器。sed 主要是用来将数据进行选取、替换、删除、新增的命令 81 | - `sed [选项] '[动作]' 文件名` 82 | - 选项 83 | - \-n 一般 sed 命令会把所有数据都输出到屏幕,如果加入此选择则只会把经过 sed 命令处理的行输出到屏幕 84 | - \-e 允许对输入数据应用多条 sed 命令编辑 85 | - \-i 用 sed 的修改结果直接修改读取数据的文件,而不是由屏幕输出 86 | - 动作 87 | - a 追加,在当前行后添加一行或多行 88 | - c 行替换,用 c 后面的字符串替换原数据行 89 | - i 插入,在当期行前插入一行或多行 90 | - d 删除,删除指定行 91 | - p 打印,输出指定的行 92 | - s 字符串替换,用一个字符串替换另外一个字符串,格式为 "行范围s/旧字符串g" 93 | - 例子 94 | - `sed '2p' student.txt` 查看文件的第二行 95 | - `sed '2,4d' student.txt` 表示删除【第2-4行】,但是不改变文件本身 96 | - `sed '2a piaoliangdecxiaoguniang' student.txt` 在第2行之后添加字符串,但是不改变字符串本身 97 | - `sed '4c xxwmpg' student.txt` 将第二行的整行替换为xxwmpg 98 | - `sed '6s/70/100/g' student.txt` 将第7行的所有70全部替换为100,如果不指定行的话会替换所有的指定字符串 99 | - `sed -e 's/fengj//g';s/cang//g' student.txt` 同时把fengj和cang替换为空 100 | 101 | ## 第3章 字符处理命令 102 | 103 | - sort 排序命令 104 | - `sort [选项] 文件名` 105 | - 选项 106 | - \-f 忽略大小写 107 | - \-n 以数值型进行排序,默认使用字符串型排序 108 | - \-r 反向排序 109 | - \-t 指定分隔符,默认的分隔符是制表符 110 | - \-k n[,m] 按照指定的字段范围排序。从第 n 字段开始,m 字段结束(默认到行尾) 111 | - 用法 112 | - `sort /etc/passwd` 用户信息正向排序 113 | - `sort -r /etc/passwd` 用户信息反向排序 114 | - `sort -t ":" -k 3,3 /etc/passwd` 指定分隔符是 ":",用第三字段开头,第三字段结尾排序,就是只用第三字段排序 115 | - `sort -n -t ":" -k 3,3 /etc/passwd` 作用同上,但把指定字符串排序改为数字排序 116 | - wc 统计命令 117 | - `wc [选项] 文件名` 118 | - 选项: 119 | - \-l 只统计行数 120 | - \-w 只统计单词数 121 | - \-m 只统计字符数 122 | - 用法: 123 | - `wc /etc/passwd` 124 | - `wc -l /etc/passwd` 125 | -------------------------------------------------------------------------------- /博客文章/Linux-Shell-基础之运算符.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux Shell 基础之运算符 3 | date: 2016-10-04 10:53:55 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## Shell 运算符 10 | 11 | - declare 声明变量 12 | - `declare [+/-][选项] 变量名` 13 | - \- 给变量设定类型属性 14 | - \-a 将变量声明为数组型 15 | - \-i 将变量声明为整数型 16 | - \-x 将变量声明为环境变量 17 | - \-r 将变量声明为只读变量 18 | - \-p 显示指定变量的被声明的类型 19 | - \+ 取消变量的类型属性 20 | - `declare -x test=123` 声明环境变量 21 | - declare -p 查询变量的属性 22 | - `declare -p` 查询所有变量的属性 23 | - `declare -p 变量名` 查询指定变量的属性 24 | - expr 或 let 数值运算工具 25 | 26 | ## Shell 运算符 例子 27 | 28 | ### Shell declare 例子 29 | ```shell 30 | # 给变量 aa 和 bb 赋值 31 | aa=11 32 | bb=22 33 | 34 | # 声明变量 cc 的类型是整数型,它的值是 aa 和 bb 的和 35 | declare -i cc=$aa+$bb 36 | ``` 37 | 38 | ### 声明数组变量例子 39 | ```shell 40 | # 定义数组 41 | movie[0]=zp 42 | movie[1]=tp 43 | declare -a movie[2]=live 44 | 45 | # 查看数组 46 | echo ${movie} 47 | echo ${movie[2]} 48 | echo ${movie[*]} 49 | ``` 50 | 51 | 52 | ## Shell 环境变量配置文件 53 | 54 | 环境变量配置文件中主要是定义对系统操作环境生效的系统默认环境变量,如 PATH 等 55 | 56 | - source 命令 57 | - `source 配置文件` 58 | - `. 配置文件` 59 | - 常用的环境变量配置文件 60 | - /etc/profile 61 | - USER 变量 62 | - LOGNAME 变量 63 | - MAIL 变量 64 | - PATH 变量 65 | - HOSTNAME 变量 66 | - HISTSIZE 变量 67 | - umask 查看系统默认权限 68 | - 文件最高权限为 666 69 | - 目录最高权限为 777 70 | - 权限不能使用数字进行换算,而必须使用字母 71 | - umask 定义的权限,是系统默认权限中准备丢弃的权限 72 | - 调用 /etc/profile.d/*sh 文件 73 | - /etc/profile.d/*sh 74 | - ~/.bash_profile 75 | - ~/.bashrc 76 | - /etc/bashrc 77 | - 其他配置文件 78 | - `~/.bash_logout` 注销时生效的环境变量配置文件 79 | - Shell 登录信息 80 | - `cat /etc/issue` 本地终端欢迎信息 81 | - `cat /etc/issue.net` 远程终端欢迎信息 82 | - 转义符在 `/etc/issue.net` 文件中不能使用 83 | - 是否显示此欢迎信息,由 SSH 的配置文件 `/etc/ssh/sshd_config` 决定,加入 "Banner /etc/issue.net" 行才能显示(需要重启 SSH 服务) 84 | - `/etc/motd` 登陆后欢迎信息,不管是本地登录,还是远程登录,都可以显示此欢迎信息 85 | -------------------------------------------------------------------------------- /博客文章/Linux-Shell-实战.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux Shell 实战 3 | date: 2016-10-03 16:11:54 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## monitor_man.sh 10 | ```shell 11 | #!/bin/bash 12 | 13 | resettem=$(tput sgr0) 14 | declare -A ssharray 15 | i=0 16 | numbers="" 17 | 18 | for script_file in `ls -I "monitor_man.sh" ./` 19 | do 20 | echo -e "\e[1;35m" "The Script:" ${i} '==>' ${resettem} ${script_file} 21 | ssharray[$i]=${script_file} 22 | numbers="${numbers} | ${i}" 23 | 24 | i=$((i+1)) 25 | done 26 | 27 | while true 28 | do 29 | read -p "Please input a number [ ${numbers} ]:" execshell 30 | if [[ ! ${execshell} =~ ^[0-9]+ ]];then 31 | exit 0 32 | fi 33 | done 34 | ``` 35 | 36 | ## system_monitor.sh 37 | ```shell 38 | #!/bin/bash 39 | 40 | clear 41 | 42 | if [[ $# -eq 0 ]] 43 | then 44 | 45 | # Define Variable reset_terminal 46 | reset_terminal=$(tput sgr0) 47 | 48 | # Check OS Type 49 | os=$(uname -o) 50 | echo -e '\E[32m' "Check OS Type" $reset_terminal $os 51 | 52 | # Check OS Release Version and Name 53 | os_name=$(cat /etc/issue|grep -e "Server") 54 | echo -e '\E[32m' "Check OS Release Version and Name" $reset_terminal $os_name 55 | 56 | # Check Architecture 57 | architecture=$(uname -m) 58 | echo -e '\E[32m' "Check Architecture" $reset_terminal $architecture 59 | 60 | # Check Kernel Release 61 | kernel_release=$(uname -r) 62 | echo -e '\E[32m' "Check Kernel Release" $reset_terminal $kernel_release 63 | 64 | # Check Hostname $HOSTNAME 65 | host_name=$(uname -n) 66 | echo -e '\E[32m' "Check Hostname" $reset_terminal $host_name 67 | 68 | # Check Internal IP 69 | internal_ip=$(hostname -I) 70 | echo -e '\E[32m' "Check Internal IP" $reset_terminal $internal_ip 71 | 72 | # Check External IP 73 | external_ip=$(curl -s http://ipecho.net/plain) 74 | echo -e '\E[32m' "Check External IP" $reset_terminal $external_ip 75 | 76 | # Check DNS 77 | name_servers=$(cat /etc/resolv.conf | grep -E "\ /dev/null && echo "Internet:Connected" || echo "Internet:Disconnected" 82 | echo -e '\E[32m' "Check if connected to Internet or not" $reset_terminal $ping 83 | 84 | # Check Logged In Users 85 | who>/tmp/who 86 | echo -e '\E[32m' "Logged In Users" && cat /tmp/who 87 | rm -f /tmp/who 88 | 89 | # Check System Memory Usages 90 | system_men_usages=$(awk '/MemTotal/{total=$2}/MemFree/{free=$2}END{print (total-free)/1024}' /proc/meminfo) 91 | apps_mem_usages=$(awk '/MemTotal/{total=$2}/MemFree/{free=$2}/^Cached/{cached=$2}/Buffers/{buffers=$2}END{print (total-free-cached-buffers)/1024}' /proc/meminfo) 92 | 93 | echo -e '\E[32m' "System Memuserages" $reset_terminal $system_men_usages 94 | echo -e '\E[32m' "Apps Memuserages" $reset_terminal $apps_mem_usages 95 | 96 | load_average=$(top -n 1 -b | grep "load average:" | awk '{print $12 $13 $14}') 97 | echo -e '\E[32m' "load averages" $reset_terminal $load_average 98 | 99 | disk_average=$(df -hP | grep -vE 'Filesystem|tmpfs' | awk '{print $1 " " $5}') 100 | echo -e '\E[32m' "disk averages" $reset_terminal $disk_average 101 | 102 | fi 103 | ``` 104 | 105 | ## check_server_sh 106 | ```shell 107 | #!/bin/bash 108 | 109 | resettem=$(tput sgr0) 110 | 111 | nginx_server='http://10.170.22.217' 112 | 113 | check_nginx_server() 114 | { 115 | status_code=curl -m 5 -s -w %{http_code} ${nginx_server} -o /dev/null 116 | 117 | if [ $status_code -eq 000 -o $status_code -ge 500 ];then 118 | echo -e '\E[32m' "Check http server error! Response status code is " $resettem $status_code 119 | else 120 | http_content=$(curl -s ${nginx_server}) 121 | echo -e '\E[32m' "Check http server ok! \n" $resettem $http_content 122 | fi 123 | } 124 | 125 | check_nginx_server 126 | ``` 127 | -------------------------------------------------------------------------------- /博客文章/Linux-定时任务.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 定时任务 3 | date: 2016-09-26 17:25:18 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## Linux 定时任务 10 | 11 | - at 一次性定时任务 12 | - crontab 循环定时任务 13 | - crontab 设置 14 | - anacron 配置 15 | 16 | ## at 一次性定时任务 17 | 18 | ```shell 19 | # at 服务是否安装 20 | chkconfig --list | grep atd 21 | 22 | # at 服务的启动 23 | service atd restart 24 | ``` 25 | 26 | at 的访问控制 27 | 28 | - 如果系统中有 /etc/at.allow 文件,那么只有写入 /etc/at.allow (白名单)文件中的用户可以使用 at 命令(/etc/at.deny 文件会被忽略) 29 | - 如果系统中没有 /etc/at.allow 文件,只有 /etc/at.deny 文件,那么写入 /etc/at.deny (黑名单)文件中的用户不能使用 at 命令。对 root 不起作用 30 | - 如果系统中这两个文件都不存在,那么只有 root 用户可以使用 at 命令 31 | 32 | 33 | at 命令 34 | 35 | - at 36 | - `at [选项] 时间` 37 | - 选项: 38 | - \-m 当 at 工作完成后,无论命令是否有输出,都用 email 通知执行 at 命令的用户 39 | - \-c 工作号:显示该 at 工作的实际内容 40 | - 时间: 41 | - HH:MM 02:30 42 | - HH:MM YYYY-MM-DD 02:30 2013-07-25 43 | - HH:MM[am|pm] [month] [date] 02:30 July 25 44 | - HH:MM[am|pm] + [minutes|hours|days|weeks] now + 5 minutes 45 | - 例子: 46 | - `at now + 2 minutes` && `/root/hello.sh >> /root/hello.log` 47 | - `at 02:00 2013-07-26` && `/bin/sync` && `/sbin/shutdown -r now` 48 | - 其他: 49 | - `atq` 查询当前服务器上的 at 工作 50 | - `atrm [工作号]` 删除指定的 at 任务 51 | 52 | 53 | ## anncron 54 | 55 | anacron 是用来保证在系统关机的时候错过的定时任务,可以在系统开机之后再执行 56 | 57 | anacron 检测周期 58 | 59 | - anacron 会使用一天,七天,一个月作为检测周期 60 | - 在系统的 /var/spool/anacron/ 目录中存在 cron.{daily,weekly,monthly} 文件,用于记录上次执行的 cron 的时间 61 | - 和当前时间做比较,如果两个时间的差值超过了 anacron 的指定时间差值,证明有 cron 任务被漏执行 62 | 63 | anacron 配置文件 64 | 65 | - vim /etc/anacrontab 66 | - `RANDOM_DELAY=45` 最大随机延迟 67 | - `START_HOURS_RANGE=3-22` anacron 的执行时间范围是 3:00 - 22:00 68 | - `1 5 cron.daily nice run-parts /etc/cron.daily` 天数 强制延迟(分钟) 工作名称 实际执行的命令 69 | 70 | anacron 执行过程(以 cron.daily 为例) 71 | 72 | - 首先读取 /var/spool/anacron/cron.daily 中的上一次 anacron 执行的时间 73 | - 和当前时间比较,如果两个时间的差值超过 1 天,就执行 cron.daily 工作 74 | - 执行这个工作只能在 03:00-22:00 之间 75 | - 执行工作时强制延迟时间为 5 分钟,再随机延迟 0-45 分钟时间 76 | - 使用 nice 命令指定默认优先级,使用 run-parts 脚本执行 /etc/cron.daily 目录中的所有可执行文件 77 | 78 | ## Crontab 简介 79 | 80 | 业务场景 81 | 82 | - 每分钟需要执行一个程序检查系统运行状态 83 | - 每天凌晨需要对过去一天的业务数据进行统计 84 | - 每个星期需要把日志文件备份 85 | - 每个月需要把数据库进行备份 86 | 87 | 概念理解 88 | 89 | **Crontab 是一个用于设置周期性被执行的任务的工具。** 90 | 91 | ## Crontab 实践 92 | 93 | - 相关工具 94 | - Mac OS 自带 SSH 95 | - 安装并检查 Crontab 服务 96 | - 检查 cron 服务 97 | - 检查 Crontab 工具是否安装:`crontab -l` 98 | - 检查 Crontab 可执行的命令:`service crond` 99 | - 检查 Crontab 服务是否启动:`service crond status` 100 | - 删除当前用户所有的定时任务: `crontab -r` 101 | - 打开定时任务列表,进行编辑:`crontab -e` 102 | - 实际上在修改目录 `/var/spool/cron/root` 下用户对应的文件 103 | - `crontab 文件名` 会把(crontab -e)文件里的内容都覆盖,使用时需要注意 104 | - 启动 Crontab 服务:`service crond start` 105 | - 安装 Crontab 服务(依次执行) 106 | - `yum install vixie-cron` 107 | - `yum install crontabs` 108 | - `tail` 命令 109 | - 作用:按照要求将指定的文件的最后部分输出到标准设备,如果文件有更新,tail 会主动刷新,确保你看到最新的文件内容 110 | - 选项 111 | - \-f 循环读取 112 | - \-q 不显示处理信息 113 | - \-v 显示详细的处理信息 114 | - \-c [数目] 显示的字节数 115 | - \-n [行数] 显示行数 116 | - \-s 与-f合用,表示在每次反复的间隔休眠 s 秒 117 | - \-q 从不输出给出文件名的首部 118 | - Crontab 的基本组成 119 | - 配置文件 120 | - 系统服务 Crond 121 | - 配置工具 Crontab 122 | - Crontab 的配置文件格式 123 | - `* * * * * Command` 124 | - 第一个星号:分钟 0~59 125 | - 第二个星号:小时 0~23 126 | - 第三个星号:日期 1~31 127 | - 第四个星号:月份 1~12 128 | - 第五个星号:星期 0~7(0、7 都是星期天) 129 | - 用法: 130 | - 每晚的 21:30 重启 apache 131 | - **`30 21 * * * service httpd restart`** 132 | - 每月 1 到 10 日的 4:45 重启 apache 133 | - **`45 4 1-10 * * service httpd restart`** 134 | - 每月 1、10、22 日的 4:45 重启 apache 135 | - **`45 4 1,10,22 * * service httpd restart`** 136 | - 每隔两分钟重启 apache 服务器 137 | - **`*/2 * * * * service httpd restart`** 138 | - 晚上 11 点到早上 7 点之间,每隔一小时重启 apache 139 | - **`0 23-7/1 * * * service httpd restart`** 140 | - 每天 18:00 至 23:00 之间每隔 30 分钟重启 apache 141 | - **`0,30 18-23 * * * service httpd restart`** 142 | - **`0-59/30 18-23 * * * service httpd restart`** 143 | - 总结: 144 | - \* 表示任何时候都匹配 145 | - 可以用 "A,B,C" 表示 A 或者 B 或者 C 时执行命令 146 | - 可以用 "A-B" 表示 A 到 B 之间时执行命令 147 | - 可以用 "*/A" 表示每 A 时间执行一次命令 148 | - Crontab 配置文件 149 | - 全局配置文件 150 | - `/etc/crontab` 查看系统级别的 Crontab 151 | - 用户配置文件 152 | - `/var/spool/cron/用户名称` 查看用户级别的 Crontab 153 | - Crontab 的日志 154 | - 监听 Crontab 执行日志:`tail -f /var/log/cron`,可以看到用户级别和系统级别的 Crontab 155 | - 查看每天的 Crontab 日志:`ls /var/log/cron*` 156 | - Crontab 常见错误 157 | - 环境变量:涉及到文件路径时写全局路径。注意声明 `#!/bin/bash` 确保脚本中环境变量能够被识别 158 | - 第三和第五个域之间执行的是 "或" 操作 159 | - 四月的第一个星期日早晨 1 时 59 分运行 a.sh:`59 1 1-7 4 * test 'date + \%w -eq 0 && /root/a.sh'` 160 | - 两个小时运行一次 161 | - 错误例子:`* 0,2,4,6,8,10,12,14,16,18,20,22 * * * date` 162 | - 正确例子:`0 */2 * * * date` 163 | - Crontab 补充 164 | - 半分钟执行命令 165 | - `date && sleep 0.5s && date` 166 | - Crontab 最小时间是 1 分钟,控制 1 分钟执行多次,本应该是同时执行,但第二条被推迟了30s执行,效果就是1分钟执行了2次 167 | - `*/1 * * * * date >> /root/test/half.log` 168 | - `*/1 * * * * sleep 30s; date >> /root/test/half.log` 169 | 170 | -------------------------------------------------------------------------------- /博客文章/Linux-服务管理.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 服务管理 3 | date: 2016-09-23 20:26:20 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | ## Linux 服务管理 9 | 10 | ### 简介与分类 11 | 12 | - #### 系统的运行级别 13 | - 查看运行级别命令:`runlevel` 14 | - 修改运行级别命令:`init 运行级别` 15 | - 系统开机进入级别:`vim /etc/inittab` ~ `id:3initdefault:` 16 | 17 | 运行级别|含义 18 | ---|--- 19 | 0 | 关机 20 | 1 | 单用户模式,主要用于系统修复 21 | 2 | 不完全的命令行模式,不含 NFS 服务 22 | 3 | 完全的命令行模式,就是标准字符界面 23 | 4 | 系统保留 24 | 5 | 图形模式 25 | 6 | 重启动 26 | 27 | - #### 服务的分类 28 | - Linux 服务 29 | - PRM 包默认安装的服务 30 | - 独立的服务 31 | - 默认配置文件位置 32 | - /etc/init.d/ 启动脚本位置 33 | - /etc/sysconfig/ 初始化环境配置文件位置 34 | - /etc/ 配置文件位置 35 | - /etc/xinetd.conf xinetd 配置文件 36 | - /etc/xinetd.d/ 基于 xineted 服务的启动脚本 37 | - /var/lib/ 服务产生的数据放在这里 38 | - /var/log/ 日志 39 | - 基于 xinetd 服务 40 | - 源码包安装的服务 41 | - 源码包安装位置,一般是 `/usr/local` 下 42 | - PRM 包安装和源码包安装的区别:两者服务安装的位置不同 43 | - #### 服务与端口 44 | - 概念:如果把 IP 地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个 IP 地址的端口可以有 65536 个 45 | - 服务与端口的对应:`cat /etc/services` 46 | - 查询系统中开启的服务:`netstat [选项]` 47 | - \-t 列出 TCP 数据 48 | - \-u 列出 UDP 数据 49 | - \-l 列出正在监听的网络服务(不包含已经连接的网络服务) 50 | - \-n 用端口号来显示服务,而不是用服务名 51 | - \-p 列出该服务的进程 ID 52 | - chkconfig 和 netstat 的区别 53 | - `chkconfig` 查看自启动命令 54 | - `netstat` 查看启动命令 55 | - #### 服务启动与自启动 56 | - 独立服务的自启动 57 | - `ntsysv` 命令图形界面管理自启动 58 | - `/etc/rc.d/rc.local` 文件修改管理自启动 59 | - `chkconfig --list` 查看服务自启动状态,可以看到所有 RPM 包安装的服务 60 | - `chkconfig [--level 运行级别] [独立服务名] [on|off]` 修改服务自启动状态 61 | - 独立服务的启动 62 | - `/etc/init.d/独立服务名 start、stop、status、restart` 63 | - `service 独立服务名 start、stop、status、restart` 64 | - 源码包安装服务的启动 65 | - 使用绝对路径,调用启动脚本来启动。不同的源码包的启动脚本不同。可以查看源码包的安装说明,查看启动脚本的方法。 66 | - 用法:`/usr/local/apache2/bin/apachectl start` 67 | - 源码包服务的自启动 68 | - `vi /etc/rc.d/rc.local` 加入 `/usr/local/apache2/bin/apachectl start` 69 | - 源码包的 Apache 服务能被 Service 命令管理启动 70 | - `ln -s /usr/local/apache2/bin/apachectl /etc/init.d/apache` 71 | - 源码包的 Apache 服务能被 chkconfig 与 ntsysv 命令管理自启动 72 | - `vi /etc/init.d/apache` 加入 `# chkconfig:35 86 76` 和 `description:source package apache` 73 | - `chkconfig --add apache` 把源码包 apache 加入 chkconfig 命令 74 | 75 | -------------------------------------------------------------------------------- /博客文章/Linux-权限管理.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 权限管理 3 | date: 2016-09-30 14:24:23 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## 基本权限 10 | 11 | - `drwxrw-r--` 12 | - d 文件类型 13 | - r 读 14 | - w 写 15 | - x 执行 16 | - rwx 代表所有者权限 17 | - rw- 代表所属组权限 18 | - r-- 代表其他人权限 19 | - 权限作用 20 | - 权限对文件的作用: 21 | - r 读取文件内容 22 | - w 编辑、新增、修改文件内容,但是不能删除文件 23 | - x 可执行 24 | - 权限对目录的作用: 25 | - r 可以查询目录下文件名(ls) 26 | - w 具有修改目录结构的权限。如新建文件和目录,删除此目录下文件和目录,重命名此目录下文件和目录,剪切(touch rm mv cp) 27 | - x 可以进入目录(cd) 28 | - 区别: 29 | - 对文件来讲,最高权限是 x 30 | - 对目录来讲,最高权限是 w 31 | - 权限命令 32 | - chmod 33 | - `chmod [选项] 模式 文件名` 34 | - 选项: 35 | - \-R 递归 36 | - 模式: 37 | - `[ugoa] [+.=] [rwx]` 38 | - `[mode=421]` 39 | - 例子: 40 | - `chmod u+x cangls.av` 41 | - `chmod a=rwx fengjie.av` 42 | - `chmod g+w,o+w furong.av` 43 | - 权限的数字表示 44 | - r 4 45 | - w 2 46 | - x 1 47 | - chgrp 48 | - `chgrp 组名 文件名` 修改文件的所属组 49 | - chown 50 | - `chown 用户名 文件名` 修改文件的所有者 51 | 52 | 53 | ## 默认权限 54 | 55 | - umask 查看默认权限 56 | - 0022 57 | - 第一位 0:文件特殊权限 58 | - 022:文件默认权限 59 | - 修改 umask 值 60 | - 临时修改 61 | - umask 0002 62 | - 永久修改 63 | - vim /etc/profile 64 | 65 | 文件的默认权限特点 66 | 67 | - 文件默认不能建立为执行文件,必须手工赋予执行权限 68 | - 文件默认权限最大为 666 69 | - 默认权限需要换算成字母再相减 70 | - 建立文件之后的默认权限,为 666 减去 umask 值 71 | - 例子: 72 | - 文件默认最大权限 666,umask 值 022 73 | - `-rw-rw-rw- 减去 -----w--w- 等于 -rw-r--r--`,结果为 644 74 | - `-rw-rw-rw- 键入 -----wx-wx 等于 -rw-r--r--`,结果为 644 75 | 76 | 77 | 目录的默认权限特点 78 | 79 | - 目录默认权限最大为 777 80 | - 默认权限需要换算成字母再相减 81 | - 建立文件之后的默认权限,为 777 减去 umask 值 82 | - 例子: 83 | - 目录默认最大权限为 777,umask 值 022 84 | - `-rwxrwxrwx 减去 -----w--w- 等于 -rwxr-xr-x`,结果为 755 85 | -------------------------------------------------------------------------------- /博客文章/Linux-系统管理.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 系统管理 3 | date: 2016-09-24 09:01:11 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | 9 | ## 进程简介 10 | 11 | 进程是正在执行的一个程序或命令,每一个进程都是一个运行的实体,都有自己的地址空间,并占用一定的系统资源 12 | 13 | 14 | ## 进程管理的作用 15 | 16 | - 判断服务器健康状态 17 | - 查看系统中所有进程 18 | - 杀死进程 19 | 20 | 21 | ## 进程的查看 22 | 23 | - #### ps 24 | - `ps aux` 查看系统中所有进程,使用 BSD 操作系统格式 25 | - `ps -le` 查看系统中所有进程,使用 Linux 标准命令模式 26 | - 选项: 27 | - a 显示一个终端的所有进程,除了会话引线 28 | - u 显示进程的归属用户及内存的使用情况 29 | - x 显示没有控制终端的进程 30 | - \-l 长格式显示。显示更加详细的信息 31 | - \-e 显示所有进程,和 -A 作用一致 32 | - ps 命令输出 33 | - USER 该进程是由哪个用户产生的 34 | - PID 该进程的 ID 号 35 | - %CPU 该进程占用 CPU 资源的百分比,占用越高,进程越耗费资源 36 | - %MEN 该进程占用物理内存的百分比,占用越高,进程越耗费资源 37 | - VSZ 该进程占用虚拟内存大小,单位 KB 38 | - RSS 该进程占用实际物理内存的大小,单位 KB 39 | - TTY 该进程是在哪个终端中运行的。其中 tty1-tty7 代表本地控制台终端,tty1-tty6 是本地的字符界面终端,tty7 是图形终端。pts/0-255 代表虚拟终端,? 代表内核运行 40 | - STAT 进程状态 41 | - R 运行 42 | - S 睡眠 43 | - T 停止状态 44 | - + 位于后台 45 | - s 包含子进程 46 | - START 该进程的启动时间 47 | - TIME 该进程占用 CPU 的运算时间,注意不是系统时间 48 | - COMMAND 产生此进程的命令名 49 | - #### pstree 50 | - `pstree [选项]` 51 | - \-p 显示进程的 PID 52 | - \-u 显示进程的所属用户 53 | - #### top 54 | - `top [选项]` 55 | - `top -b -n 1 > top.log` 把每秒的 top 信息写进 `top.log` 文件中 56 | - 选项: 57 | - \-d 秒数 指定 top 命令每隔几秒更新。默认是 3 秒 58 | - \-b 使用批处理模式输出。一般和 "-n" 选项合用 59 | - \-n 次数 指定 top 命令执行的次数。一般和 "-b" 选项合用 60 | - top 命令交互模式可以执行命令 61 | - ? 或 h 显示帮助 62 | - M 以内存使用率排序 63 | - P 以 CPU 使用率排序,默认此项 64 | - N 以 PID 排序 65 | - q 退出 top 66 | - top 参数意义 67 | - 第一行系统信息: 68 | - `12:26:46` 系统当前时间 69 | - `up 1 day,13:32` 系统运行时间,本机已运行了 1 天 13 小时 32 分钟 70 | - `2 users ` 当前系统登录了用户 71 | - `load average: 0.00,0.00,0.00` 系统在之前 1 分钟,5 分钟,15 分钟的平均负载。一般认为小于1时,负载较小。如果大于 1,系统已经超出负荷 72 | - 第二行进程信息: 73 | - `Tasks: 95 total` 系统中的进程总数 74 | - `1 running` 正在运行的进程数 75 | - `94 sleeping ` 睡眠的进程 76 | - `0 stopped` 正在停止的进程 77 | - `0 zombie` 僵尸进程。如果不是 0,需要手工检查僵尸进程 78 | - 第三行 CPU 信息: 79 | - `Cpu(s) 0.1%us` 用户模式占用的 CPU 百分比 80 | - `0.1%sy` 系统模式占用的 CPU 百分比 81 | - `0.0%ni` 改变过优先级的用户进程占用的 CPU 百分比 82 | - `99.7%id` 空闲 CPU 的 CPU 百分比 83 | - `0.1%wa` 等待输入 / 输出的进程的占用 CPU 百分比 84 | - `0.0%hi` 硬中断请求服务占用的 CPU 百分比 85 | - `0.1%si` 软中断请求服务占用的 CPU 百分比 86 | - `0,0%st` 虚拟时间百分比 87 | - 第四行内存信息: 88 | - `Mem: 635344k total` 物理内存的总量,单位 KB 89 | - `571504k used` 已经使用的物理内存数量 90 | - `53840k free` 空闲的物理内存数量 91 | - `65800k buffers` 作为缓冲的内存数量 92 | - 第五行交换分区信息: 93 | - `Swap: 524280k total` 交换分区(虚拟内存)的总大小 94 | - `Ok used` 已经使用的交互分区的大小 95 | - `524280k free` 空闲交换分区的大小 96 | - `409280k cached` 作为缓存的交互分区的大小 97 | 98 | 99 | ## 杀死进程 100 | 101 | - kill 102 | - `kill -l` 查看可用的进程信号 103 | - `kill -1 进程 ID` 重启进程 104 | - `kill -9 进程 ID` 强制杀死进程 105 | - killall 106 | - `killall [选项] [信号] 进程名` 107 | - 选项: 108 | - \-i 交互式,询问是否要杀死某个进程 109 | - \-I 忽略进程名的大小写 110 | - pkill 111 | - `pkill [选项] [信号] 进程名` 112 | - 选项: 113 | - \-t 终端号:按照终端号踢出用户 114 | - 用法: 115 | - `pkill -9 -t tty1` 116 | 117 | 118 | ## 修改进程优先级 119 | 120 | Linux 操作系统是一个多用户、多任务的操作系统,Linux 系统中通知运行着非常多的进程。但是 CPU 在同一个时钟周期内只能运算一个指令。进程优先级决定了每个进程处理的先后顺序呢。 121 | 122 | 执行命令 `ps -le` 返回的结果中,PRI 代表 Priority,NI 代表 Nice。这两个值都是优先级,数字越少代表改进程优先级越高。 123 | 124 | 用户可以修改 NI 值,注意事项如下: 125 | 126 | - NI 值的范围是 -20 到 19 127 | - 普通用户调整 NI 值的范围是 0 到 19,而且只能调整自己的进程 128 | - 普通用户只能调高 NI 值,而不能降低,如原本 NI 值为0,则只能调整为大于 0 129 | - root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程 130 | - PRI(最终值)= PRI(原始值)+ NI 131 | - 用户只能修改 NI 的值,不能直接修改 PRI 132 | 133 | 修改进程优先级的命令如下: 134 | 135 | - nice 136 | - `nice [选项] 命令` nice 的命令可以给新执行的命令直接赋予 NI 值,但是不能修改已经存在进程的 NI 值 137 | - 选项: 138 | - \-n NI 值:给命令赋予 NI 值 139 | - 用法: 140 | - `nice -n -5 service httpd start` 141 | - renice 142 | - `renice [优先级] 进程 ID` renice 命令是修改已经存在进程的 NI 值的命令 143 | - 用法: 144 | - `renice -10 2125` 145 | 146 | 147 | ## 工作管理 148 | 149 | 工作管理指的是在单个登录终端中(也就是登录的 Shell 界面中)同时管理多个工作的行为 150 | 151 | 工作管理注意事项如下: 152 | 153 | - 当前的登录终端,只能管理当前终端的工作,而不能管理其他登录终端的工作 154 | - 放入后台的命令必须可以持续运行一段时间,这样我们才能扑捉和操作这个工作 155 | - 放入后台执行的命令不能和前台用户有交互或需要前台输入,否则放入后台只能暂停,而不能执行 156 | 157 | ## 工作管理方法 158 | 159 | ```shell 160 | # 把命令放入后台,并在后台执行 161 | top & 162 | 163 | # 按下 ctrl + z 快捷键,放在后台暂停 164 | top 165 | ``` 166 | 167 | - jobs 168 | - `josb [选项]` 169 | - 选项: 170 | - \-l 显示工作的 PID 171 | - \+ 号代表最近一个放入后台的工作,也是工作恢复时,默认恢复的工作;- 号代表倒数第二个放入后台的工作 172 | - fg 173 | - `fg %工作号` 将后台暂停的工作恢复到前台执行,注意 % 号可以省略,但是注意工作号和 PID 的区别 174 | - bg 175 | - `bg %工作号` 后台恢复执行的命令,是不能和前台有交互的,否则不能恢复到后台执行 176 | 177 | ## 后台命令脱离登录终端执行的方法 178 | 179 | - 第一种方法是把需要后台执行的命令加入 /etc/rc.local 文件 180 | - 第二种方法是使用系统定时任务,让系统在指定的时间执行某个后台命令 181 | - 第三种方法是使用 nohup 命令 182 | 183 | ## 系统资源查看 184 | 185 | - vmstat 186 | - `vmstat [刷新延时 刷新次数]` 187 | - 用法: 188 | - `vmstat 1 3` 189 | - 参数: 190 | - procs:进程信息字段 191 | - r 等待运行的进程数,数量越大,系统越繁忙 192 | - b 不可被唤醒的进程数量,数量越大,系统越繁忙 193 | - memory:内存信息字段 194 | - free 空闲的内存容量,单位 KB 195 | - buff 缓冲的内存容量,单位 KB 196 | - cache 缓存的内存容量,单位 KB 197 | - swpd 虚拟内存的使用情况,单位 KB 198 | - 缓冲和缓存的区别:缓存是用来加速数据读取的,缓冲是用来加速数据写入的 199 | - swap:交换分区的信息字段 200 | - si 从磁盘中交换到内存中数据的数量,单位 KB 201 | - so 从内存中交换到磁盘中数据的数量,单位 KB。此两个数越大,证明数据需要经常在磁盘和内存之间交换,系统性能越差。 202 | - io:磁盘读写信息字段 203 | - bi 从块设备读入数据的总量,单位是块 204 | - bo 写到块设备的数据的总量,单位是块。此两个数越大,代表系统的 I/O 越繁忙 205 | - system:系统信息字段 206 | - in 每秒被中断的进程次数 207 | - cs 每秒进行的事件切换次数。此两个数越大,代表系统与接口设备的通信非常繁忙 208 | - CPU:CPU 信息字段 209 | - us 非内核进程消耗 CPU 运算时间的百分比 210 | - sy 内核进程消耗 CPU 运算时间的百分比 211 | - id 空闲 CPu 的百分比 212 | - wa 等待 I/O 所消耗的 CPU 百分比 213 | - st 被虚拟机消耗的 CPU 百分比 214 | - dmesg 215 | - `dmesg` 显示系统内核信息 216 | - free 217 | - `free [选项]` 查看内存使用状态 218 | - 选项: 219 | - -\b 以字节为单位显示 220 | - -\k 以 KB 为单位显示,默认就是以 KB 为单位显示 221 | - -\m 以 MB 为单位显示 222 | - -\g 以 GB 为单位显示 223 | - 参数: 224 | - 第一行:total 是总内存数,used 是已经使用的内存数,free 是空闲的内存数,shared 是多个进程共享的内存总数,buffers 是缓冲内存数,cached 是缓存内存数。默认单位是 KB 225 | - 第二行:-/buffers/cache 的内存数,相当于第一行的 used-buffers-cached。\+/buffers/cache 的内存数,相当于第一行的 free + buffers + cached 226 | - 第三行:total 是 swap 的总数;used 是已经使用的 swap 数,free 是空闲的 swap 数。默认单位是 KB 227 | - cat /proc/cpuinfo 228 | - 查看 CPU 信息 229 | - uptime 230 | - 显示系统的启动时间和平均负载,也就是 top 命令的第一行。 231 | - uname 232 | - `uname [选项]` 233 | - 选项: 234 | - \-a 查看系统所有相关信息 235 | - \-r 查看内核版本 236 | - \-s 查看内核名称 237 | - file /bin/ls 238 | - 查看系统操作位数 239 | - lsb_release -a 240 | - 查询当前 Linxu 系统的发行版本 241 | - lsof 242 | - `lsof [选项]` 243 | - 选项: 244 | - \-c 字符串:只列出以字符串开头的进程打开的文件 245 | - \-u 用户名:只列出某个用户的进程打开的文件 246 | - \-p PID:列出某个 PID 进程打开的文件 247 | - 例子: 248 | - `lsof | more` 249 | - `lsof /sbin/init` 250 | - `lsof -c httpd` 251 | - `lsof -u root` 252 | -------------------------------------------------------------------------------- /博客文章/Linux-网络基础.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 网络基础 3 | date: 2016-09-20 20:33:54 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | ## 网络基础 9 | 10 | ### ISO / OSI 七层模型简介 11 | 12 | - ISO:国际标准化组织 13 | - OSI:开放系统互联模型 14 | - OSI 的七层模型 15 | - 应用层 - APDU 16 | - APDU 是应用协议数据单元 17 | - 表示层 - PPDU 18 | - PPDU 是表示层协议数据单元 19 | - 会话层 - SPDU 20 | - SPDU 是会话协议数据单元 21 | - 传输层 - TPDU 22 | - TPDU 是传输协议数据单元 23 | - 网络层 - 报文 24 | - IP 地址负责外网通信 25 | - 数据链路层 - 帧 26 | - MAC 地址负责局域网通信 27 | - 物理层 - 比特 28 | - 数据发送时候,从上层向下层传递;数据接收时候,从下层向上层传递。 29 | 30 | 31 | ### ISO / OSI 七层模型详解 32 | 33 | - 应用层 - APDU 34 | - 用户接口 35 | - 表示层 - PPDU 36 | - 数据的表现形式、特定功能的实现如-加密 37 | - 会话层 - SPDU 38 | - 对应用会话的管理、同步 39 | - 传输层 - TPDU 40 | - 可靠与不可靠的传输、传输前的错误检测、流控 41 | - 网络层 - 报文 42 | - 提供逻辑地址、选路 43 | - 数据链路层 - 帧 44 | - 城帧、用 MAC 地址访问媒介、错误检测与修正 45 | - 物理层 - 比特 46 | - 设备之间的比特流的传输、物理接口、电器特征等 47 | 48 | ### TCP / IP 四层模型 49 | 50 | - TCP / IP 四层模型 51 | - 应用层 (对应 OSI 模型的应用层、表示层、会话层) 52 | - 应用层对应于 OSI 参考模型的应用层、表示层、会话层,为用户提供所需要的各种服务,例如:FTP、Telnet、DNS、SMTP 等 53 | - 传输层 (对应 OSI 模型的传输层) 54 | - 传输层对应于 OSI 模型的传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。该层定义了两个主要的协议:传输控制协议(TCP)和用户数据协议(UDP) 55 | - 网际互联层(对应 OSI 模型的网络层) 56 | - 网际互联对应于 OSI 模型的网络层,主要解决主机到主机的通信问题。它所包含的协议涉及数据包在整个网络上的逻辑传输。该层有三个主要协议:网际协议(IP)、互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。 57 | - 网络接口层(对应 OSI 模型的数据链路层、物理层) 58 | - 网络接口层与 OSI 模型中的物理层和数据链路层相对应。它负责监视数据在主机和网络之间的交换。事实上,TCP /IP 本身并未定义该层的协议,而由参与互连的各网络使用自己的物理层和数据链路层协议,然后与 TCP / IP 的网络接口层进行连接。地址解析协议(ARP)工作在此层,即 OSI 参考模型的数据链路层。 59 | 60 | TCP / IP 三次握手(两军问题) 61 | 62 | 数据封装过程 63 | 64 | 应用数据|字节流(数据) 65 | ---|--- 66 | 应用层|FTP + 数据 67 | 传输层|TCP 头 + FTP + 数据 68 | 网络层|IP 头 + TCP 头 + FTP + 数据 69 | 数据链路层|以太帧头 + (IP 头 + TCP 头 + FTP + 数据) = IP 数据报 70 | 最后|以太帧头 + 目的地址 + 源地址 + 包类型 + 包数据(IP 数据报) + CRC 71 | 72 | ### IP地址详解 73 | 74 | - IP 包头 75 | - 第一行 76 | - 版本(4) 77 | - 头部长度(4) 78 | - 优先级和服务类型(8) 79 | - 总长度(16) 80 | - 第二行 81 | - 标识(16) 82 | - 标志(3) 83 | - 片偏移(13) 84 | - 第三行 85 | - 生存时间(8) 86 | - 协议(8) 87 | - 头部校验和(16) 88 | - 第四行 89 | - 源 IP 地址(32) 90 | - 第五行 91 | - 目的 IP 地址(32) 92 | - 第六行 93 | - 选项 94 | - 第七行 95 | - 数据 96 | 97 | IP 地址分类 98 | 99 | 网络类别|最大网络数|IP 地址范围|最大主机数|私有 IP 地址范围 100 | ---|---|---|---|--- 101 | A|126| 1.0.0.0 -- 126.255.255.255|2^24-2|10.0.0.0 -- 10.255.255.255 102 | B|16384| 128.0.0.0 -- 191.255.255.255|2^16-2|172.16.0.0 -- 172.31.255.255 103 | C|2098152| 192.0.0.0 -- 223.255.255.255|2^8-2|192.168.0.0 -- 192.168.255.255 104 | 105 | ### 子网掩码 106 | 107 | ### 网关作用 108 | 109 | - 网关(Gateway)又称为网间连接器、协议转换器 110 | - 网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连 111 | - 网关既可以用于广域网互连,也可以用于局域网互连 112 | - 网关是一种充当转换重任的服务器或路由器 113 | 114 | ### 端口作用 115 | 116 | - TCP 协议包头 117 | - 第一行 118 | - 源端口(16) 119 | - 目的端口(16) 120 | - 第二行 121 | - 序列号(32) 122 | - 第三行 123 | - 确认号(32) 124 | - 第四行 125 | - 数据偏移(4) 126 | - 保留(6) 127 | - 标志(6) 128 | - 窗口(16) 129 | - 第五行 130 | - 校验和(16) 131 | - 紧急指针(16) 132 | - 第六行 133 | - 选项 134 | - 第七行 135 | - 数据 136 | - UDP 协议包头 137 | - 第一行 138 | - 源端口(16) 139 | - 目的端口(16) 140 | - 第二行 141 | - 长度(16) 142 | - 校验和(16) 143 | - 第三行 144 | - 数据 145 | 146 | 常见端口号 147 | 148 | - FTP(文件传输协议):20 21 149 | - SSH(安全 Shell 协议):22 150 | - TELNET(远程登录协议):23 151 | - DNS(域名系统):53 152 | - HTTP(超文本传输协议):80 153 | - SMTP(简单邮件传输协议):25 154 | - POP3(邮局协议 3 代):110 155 | 156 | 查看本机启用的端口 157 | 158 | - netstat -an 159 | - \-a 查看所有连接和监听端口 160 | - \-n 显示 IP 地址和端口号,而不显示域名和服务名 161 | 162 | 163 | ### DNS 作用 164 | 165 | - DNS 的作用:将域名解析为 IP 地址 166 | - 客户机向 DNS 服务器发送域名查询请求 167 | - DNS 服务器告知客户机 Web 服务器的 IP 地址 168 | - 客户机与 Web 服务器通信 169 | - DNS 的查询类型 170 | - 从查询方式上分 171 | - 递归查询 172 | - 要么做出查询成功响应,要么做出查询失败的响应,一般客户机和服务器之间属于递归查询,即当客户机向 DNS 服务器发出请求后,若 DNS 服务器本身不能解析,则会向另外的 DNS 服务器发出查询请求,得到结果后转交给客户机。 173 | - 迭代查询 174 | - 服务器收到一次迭代查询回复一次结果,这个结果不一定是目标 IP 与域名的映射关系,也可以是其他 DNS 服务器的地址 175 | - 从查询内容上分 176 | - 正向查询由域名查找 IP 地址 177 | - 反向查询由 IP 地址查找域名 178 | 179 | 180 | ## Linux 网络配置 181 | 182 | ### IP 地址配置 183 | 184 | ```shell 185 | # 查看与配置网络状态命令 186 | ifconfig 187 | 188 | # 临时设置 eth0 网卡的 IP 地址与子网掩码 189 | ifconfig eth0 192.168.0.200 netmask 255.255.255.0 190 | 191 | # setup 图形界面(红帽专用) 192 | setup 193 | ``` 194 | 195 | ### 使用文件配置 IP 地址 196 | 197 | 网卡信息文件 198 | ```shell 199 | vim /etc/sysconfig/network-scripts/ifcfg-eth0 200 | ``` 201 | 202 | 主机名文件 203 | ```shell 204 | vim /etc/sysconfig/network 205 | ``` 206 | 207 | ## Linux 网络命令 208 | 209 | ### 网络环境查看命令 210 | 211 | - `ifconfig` 查看与配置网络状态命令 212 | - 只能看到 IP 和子网掩码,看不到网关和 DNS 213 | - `ifdown 网卡设备名称` 禁用指定网卡设备 214 | - `ifup 网卡设置名称` 启用指定网卡设备 215 | - `netstat 选项` 216 | - \-t 列出 TCP 协议端口 217 | - \-u 列出 UDP 协议端口 218 | - \-n 不使用域名与服务名,而是用 IP 地址和端口号 219 | - \-l 仅列出在监听状态网络服务 220 | - \-a 列出所有的网络连接 221 | - `route -n` 查看路由列表 222 | - `route add default gw 192.168.1.1` 临时设定网关 223 | - `nslookup [主机名或 IP]` 进行域名与 IP 地址解析 224 | - `nslookup >server` 查看本机 DNS 服务器 225 | 226 | ### 网络测试命令 227 | 228 | - `ping [选项] [IP 或域名]` 探测指定 IP 或域名的网络状况 229 | - \-c 次数 指定 ping 包的次数 230 | - `telnet [域名或 IP] [端口]` 远程管理与端口探测命令 231 | - `wget URL` 下载命令 232 | - `tcpdump -i eth0 -nnX port 21` 233 | - \-i 指定网卡接口 234 | - \-X 以十六进制和 ASCII 码显示数据包内容 235 | - \-nn 将数据包中的域名与服务转为 IP 和端口 236 | - port 指定监听的端口 237 | 238 | -------------------------------------------------------------------------------- /博客文章/Linux-软件安装.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linux 软件安装 3 | date: 2016-09-27 22:29:56 4 | tags: Linux 5 | category: Linux 6 | --- 7 | 8 | ## 软件包管理简介 9 | 10 | - 软件包分类 11 | - 源码包 12 | - 优点: 13 | - 开源,如果有足够的能力,可以修改代码 14 | - 可以自由选择所需的功能 15 | - 软件是编译安装,所以更加适合自己的系统,更加稳定也效率更高 16 | - 卸载方便 17 | - 缺点: 18 | - 安装步骤较多,尤其安装较大的软件集合时,容易出现拼写错误 19 | - 编译过程时间较长,安装比二进制安装时间长 20 | - 编译安装一旦报错,新手很难解决安装错误 21 | - 二进制包(RPM 包、系统默认包) 22 | - 优点: 23 | - 包管理系统简单,只通过几个命令就可以实现包的安装、升级、查询和卸载 24 | - 安装速度比源码包安装快得多 25 | - 缺点: 26 | - 经过编译,不再可以看到源代码 27 | - 功能选择不如源码包灵活 28 | - 依赖性 29 | - 脚本安装包(本质是源码包和二进制包) 30 | - 优点:安装简单、快捷 31 | - 缺点:完全丧失了自定义性 32 | 33 | 34 | ## RPM 命令管理 35 | 36 | ### RPM 包命令规则 37 | 38 | - `httpd-2.2.15-15.el6.centos.1.i686.rpm` 39 | - httpd:软件包名 40 | - 2.2.15:软件版本 41 | - 15:软件发布的次数 42 | - el6.centos:适合的 Linux 平台 43 | - i686:适合的硬件平台 44 | - rpm:rpm 包扩展名 45 | 46 | RPM 包依赖性 47 | 48 | - 树形依赖:a->b->c 49 | - 环形依赖:a->b->c->a 50 | - 模块依赖:查询网站:www.rpmfind.net 51 | 52 | 53 | ### RPM 安装命令 54 | 55 | 包全名与包名 56 | 57 | - 包全名:操作的包是没有安装的软件包时,使用包全名。而且要注意路径。 58 | - 包名:操作已经安装的软件包时,使用包名,是搜索 /var/lib/rpm/ 中的数据库 59 | 60 | RPM 包安装 61 | 62 | - 安装 63 | - `rpm [选项] 包全名` 64 | - 选项: 65 | - \-i(install)安装 66 | - \-v(verbose)显示详细信息 67 | - \-h(hash)显示进度 68 | - \--nodeps 不检测依赖性 69 | - 升级 70 | - `rpm -Uvh 包全名` 71 | - 选项: 72 | - \-U 升级 73 | - 卸载 74 | - `rpm -e 包名` 75 | - 选项: 76 | - \-e 卸载 77 | - --nodeps 不检查依赖性 78 | - 查询 79 | - `rpm -qa` 查询所有已经安装的 RPM 包 80 | - `rpm -q 包名` 查询包是否安装 81 | - `rpm -qi 包名` 查询软件包详细信息 82 | - `rpm -ql 包名` 查询包中文件安装位置 83 | - RPM 包默认安装路径 84 | - /etc 配置文件安装目录 85 | - /usr/bin 可执行命令安装目录 86 | - /usr/lib 程序所使用的函数库保存位置 87 | - /usr/share/doc 基本的软件使用手册保存位置 88 | - /usr/share/man/ 帮助文件保存位置 89 | - `rpm -qf 系统文件名` 90 | - 选项: 91 | - \-f 查询系统文件属于哪个软件包(file) 92 | - `rpm -qR 包名` 93 | - 选项: 94 | - \-R 查询软件包的依赖性 95 | - \-p 查询未安装包信息 96 | 97 | ## Yum 命令管理 98 | 99 | - 查询 100 | - `yum list` 查询所有可用软件包列表 101 | - `yum search 关键字` 搜索服务器上所有和关键字相关的包 102 | - 安装 103 | - `yum -y install 包名` 104 | - 选项: 105 | - install 安装 106 | - \-y 自动回答 yes 107 | - 升级 108 | - `yum -y update 包名` 109 | - 选项: 110 | - update 升级 111 | - \-y 自动回答 yes 112 | - 卸载 113 | - `yum -y remove 包名` 114 | - 选项: 115 | - remove 卸载 116 | - \-y 自动回答 yes 117 | - 组管理 118 | - `yum grouplist` 列出所有可用的软件组列表 119 | - `yum groupinstall 软件组名` 安装指定软件组,组名可以由 grouplist 查询出来 120 | - `yum groupremove 软件组名` 卸载指定软件组 121 | 122 | ## 源码包安装 123 | 124 | 源码包安装位置需要手动指定,一般安装在 `/usr/local/软件名` 125 | 126 | 安装源码包之前需要安装 C 语言编译器 127 | 128 | 源码包安装过程 129 | 130 | - 下载源码包 131 | - 解压缩下载的源码包 132 | - 进入解压缩目录 133 | - `./configure 软件配置与检查` 134 | - 定义需要的功能选项 135 | - 检测系统环境是否符合安装要求 136 | - 把定义好的功能选项和检测系统环境的信息都写入 Makefile 文件,用于后续的编辑 137 | - `make` 编译 138 | - `make clean` 如果编译错误,尝试执行 `make clean` 139 | - `make install` 编译安装 140 | 141 | 源码包不需要卸载命令,直接删除安装目录即可。不会遗留垃圾文件 142 | 143 | -------------------------------------------------------------------------------- /博客文章/LuisEdware-版-PHP-编程规范(遵循-PSR-1-PSR-2).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: LuisEdware 版 PHP 编程规范(遵循 PSR-1/PSR-2) 3 | date: 2017-02-11 16:23:06 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 八荣八耻 9 | 10 | - 以「动手实践」为荣, 以「只看不练」为耻; 11 | - 以「打印日志」为荣, 以「单步跟踪」为耻; 12 | - 以「空格缩进」为荣, 以「制表缩进」为耻; 13 | - 以「单元测试」为荣, 以「人工测试」为耻; 14 | - 以「模块复用」为荣, 以「复制粘贴」为耻; 15 | - 以「多态应用」为荣, 以「分支判断」为耻; 16 | - 以「优雅高效」为荣, 以「冗余拖沓」为耻; 17 | - 以「总结分享」为荣, 以「跪求其解」为耻。 18 | 19 | ## 代码风格 20 | 21 | 编程开发时,需要遵循 PSR 规范如下: 22 | 23 | - PSR 2(编码风格规范):[https://laravel-china.org/topics/2079](https://laravel-china.org/topics/2079) 24 | - PSR 4(自动加载规范):[https://laravel-china.org/topics/2081](https://laravel-china.org/topics/2081) 25 | 26 | ## 目录与文件 27 | 28 | - 目录使用小写加下划线; 29 | - 类库、函数文件统一以 `.php` 为后缀; 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 | ```php 56 | nameTest = new TestClass(); 124 | // 链式操作 125 | $this->nameTest->functionOne() 126 | ->functionTwo() 127 | ->functionThree(); 128 | // 一段代码逻辑执行完毕 换行 129 | // code... 130 | } 131 | 132 | /** 133 | * 成员方法名称 134 | * 135 | * 成员方法描述 136 | * 137 | * @param string $value 形参名称/描述 138 | * 139 | * @return 返回值类型 返回值描述 140 | */ 141 | public static function staticFunction($value = '')// static位于修饰符之后 142 | { 143 | // code... 144 | } 145 | 146 | /** 147 | * 成员方法名称 148 | * 149 | * 成员方法描述 150 | * 151 | * @param string $value 形参名称/描述 152 | * 153 | * @return 返回值类型 返回值描述 154 | * 返回值类型:string,array,object,mixed(多种,不确定的),void(无返回值) 155 | */ 156 | public function testFunction($value = '')// 成员方法必须小写开头驼峰 157 | { 158 | // code... 159 | } 160 | 161 | /** 162 | * 成员方法名称 163 | * 164 | * 成员方法描述 165 | * 166 | * @param string $value 形参名称/描述 167 | * 168 | * @return 返回值类型 返回值描述 169 | */ 170 | abstract public function abstractFunction($value = ''); 171 | 172 | /** 173 | * 成员方法名称. 174 | * 成员方法描述 175 | * 176 | * @param string $value 形参名称/描述 177 | * 178 | * @return 返回值类型 返回值描述 179 | */ 180 | final public function finalFunction($value = '')// final位于修饰符之前 181 | { 182 | // code... 183 | } // abstract位于修饰符之前 184 | 185 | /** 186 | * 成员方法名称 187 | * 188 | * 成员方法描述 189 | * 190 | * @param string $paramOne 形参名称/描述 191 | * @param string $paramTwo 形参名称/描述 192 | * @param string $paramThree 形参名称/描述 193 | * @param string $paramFour 形参名称/描述 194 | * @param string $paramFive 形参名称/描述 195 | * @param string $paramSix 形参名称/描述 196 | * 197 | * @return 返回值类型 返回值描述 198 | */ 199 | public function tooLangFunction( 200 | $paramOne = '', // 变量命名可小写开头驼峰或者下划线命名 201 | $paramTwo = '', 202 | $paramThree = '', 203 | $paramFour = '', 204 | $paramFive = '', 205 | $paramSix = '' 206 | )// 参数过多换行 207 | { 208 | if ($paramOne === $paramTwo) {// 控制结构=>后加空格,同{一行,(右边和)左边不加空格 209 | // code... 210 | } 211 | 212 | switch ($paramThree) { 213 | case 'three': 214 | // code... 215 | break; 216 | default: 217 | // code... 218 | break; 219 | } 220 | 221 | do { 222 | // code... 223 | } while ($paramFour <= 10); 224 | 225 | while ($paramFive <= 10) { 226 | // code... 227 | } 228 | 229 | for ($i = 0; $i < $paramSix; $i++) { 230 | // code... 231 | } 232 | } 233 | 234 | /** 235 | * 成员方法名称 236 | * 237 | * 成员方法描述 238 | * 239 | * @param string $value 形参名称/描述 240 | * 241 | * @return 返回值类型 返回值描述 242 | */ 243 | private function _privateTestFunction($value = '')// 私有成员方法【个人建议】下划线小写开头驼峰 244 | { 245 | // code... 246 | } 247 | } 248 | ``` 249 | -------------------------------------------------------------------------------- /博客文章/MySQL-5-7-版本新特性.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MySQL 5.7 版本新特性 3 | date: 2016-09-12 21:12:40 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | ## 第 1 章 MySQL 服务功能增强 9 | 10 | 11 | ### 1-1 初始化方式变更 12 | 13 | MySQL 5.7 之前 14 | `scripts/mysql_install_db \ 15 | --datadir=/data/sql_data \ 16 | --user=mysql --basedir=/home/mysql` 17 | 18 | MySQL 5.7 之后 19 | `bin/mysql --initialize --user = mysql \ 20 | --basedir=/home/mysql \ 21 | --datadir=/home/mysql/data` 22 | 23 | ### 1-3 旧版本支持为表增加计算列演练 24 | 25 | MySQL 5.7 之前,需要增加一个插入触发器和更新触发器来实现计算列的功能,或者是一个视图来实现计算列的功能 26 | 27 | MySQL 5.7 之后,在 `CREATE TABLE` 及 `ALTER TABLE` 语句中支持增加计算列的方式 28 | ```sql 29 | col_name data_type [GENERATED ALWAYS] AS (expression) 30 | [VIRTUAL | STORED] [UNIQUE [KEY]] [COMMENT comment] 31 | [[NOT] NULL] [[PRIMARY] KEY] 32 | ``` 33 | 34 | ### 1-4 MySQL5.7支持为表增加计算列实际演练 35 | 36 | ```sql 37 | CREATE TABLE t(id int AUTO_INCREMENT not null,c1 int,c2 int,c3 int as (c1+c2),PRIMARY KEY (id)); 38 | 39 | INSERT INTO t(c1,c2) VALUES(1,2); 40 | 41 | SELECT * FROM t; 42 | 43 | UPDATE t set c1 = 5 WHERE id = 1; 44 | 45 | SELECT * FROM t; 46 | ``` 47 | 48 | ### 1-5 引入JSON列类型及相关函数 49 | 50 | MySQL 5.7 之前 51 | 52 | 只能在 varchar 或是 text 等字符类型的列中存储 JSON 类型的字符串,并通过程序解析使用 JSON 字符串。 53 | 54 | MySQL 5.7 之后 55 | 56 | 增加了 JSON 列类型及以 JSON 开头的相关处理函数,如 JSON_TYPE(),JSON_OBJECT(),JSON_MERGE() 等。 57 | ``` 58 | SELECT JSON_ARRAY(1,2,3,now()); 59 | SELECT JSON_OBJECT('keyA',1,'keyB',2); 60 | ``` 61 | 62 | ## 第 2 章 Replication 相关增强 63 | 64 | ### 2-1 支持多源复制 65 | 66 | 略 67 | 68 | ### 2-2 基于库或是逻辑锁的多线程复制 69 | 70 | MySQL 5.7 之前 71 | 72 | 从 MySQL 5.6 开始支持多线程复制,只不过是对于每一个库一个复制线程。 73 | 74 | MySQL 5.7 之后 75 | 76 | MySQL 5.7 后对多线程复制功能进行了加强,增加了 `slave_paraller_type` 参数可以控制并发同步是基于 `database` 还是 `logical_clock` 77 | 78 | ### 2-3 在线变更复制方式 79 | 80 | MySQL 5.7 之前 81 | 82 | 要把基于日志点的复制方式变为基于 gtid 的复制方式或是把基于 gtid 的复制方式变成基于日志点的复制方式必须要重启 master 的服务器 83 | 84 | MySQL 5.7 之后 85 | 86 | 允许在线变列复制的方式,而不用重启 master 服务器 87 | 88 | 89 | ## 第 3 章 InnoDB 引擎增强 90 | 91 | ### 3-1 支持缓冲池大小在线变更 92 | 93 | MySQL 5.7 之前 94 | 95 | 要变更 `innodb_buffer_pool` 大小必须更改 `my.cnf` 文件后重启数据库服务器 96 | 97 | MySQL 5.7 之后 98 | 99 | `innodb_buffer_pool_size` 参数变为动态参数,可以在线调整 `innodb` 缓存池的大小 100 | 101 | ### 3-2 增加 `innodb_buffer_pool` 导入导出功能 102 | 103 | MySQL 5.7 之后,新增以下参数控制 `innodb_buffer_pool` 的导入导出 104 | ``` 105 | innodb_buffer_pool_dump_pct 106 | innodb_buffer_pool_dump_now 107 | innodb_buffer_pool_dump_at_shutdown 108 | innodb_buffer_pool_load_at_startup 109 | innodb_buffer_pool_load_now 110 | ``` 111 | 112 | ### 3-3 支持为 `innodb` 表建立表空间 113 | 114 | MySQL 5.7 之前 115 | 116 | 具有系统表空间及可以为每个表建立一个独立的表空间 117 | 118 | 119 | MySQL 5.7 之后 120 | 121 | 支持 `CREATE TABLESPACE` 语法为一个表或多个表建立共用的表空间 122 | 123 | 124 | ## 第 4 章 安全及管理方面增强 125 | 126 | ### 4-1 不再支持old_password认证 127 | 128 | ### 4-2 增加账号默认过期时间 129 | 130 | `show variables like 'default_password_lifetime';` 131 | 132 | ### 4-3 加强了对账号的管理功能 133 | 134 | ### 4-4 增加了 sys 管理数据库 135 | 136 | -------------------------------------------------------------------------------- /博客文章/PHP Web Application 运行流程、概念术语、机制原理和代码实践.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Web Application 运行流程、概念术语、机制原理和代码实践 3 | date: 2016-06-10 20:55:55 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | 本文的目的在于了解 PHP 处理 HTTP 请求的过程 9 | 10 | ## 运行流程 11 | 12 | 1. 客户端做出请求操作(输入网址、点击链接、提交表单)。 13 | 2. 向客户端设定的 DNS 服务器请求 IP 地址。 14 | 3. 客户端根据 DNS 服务器返回 IP 地址采用三次握手与服务端建立 TCP 连接。 15 | 4. TCP 连接成功后,客户端向服务端发送 HTTP 请求。 16 | 5. 服务端的 Web Server 会判断 HTTP 请求的资源类型,进行内容分发处理;如果请求的资源为 PHP 文件,服务端软件会启动对应的 CGI 程序进行处理,并返回处理结果。 17 | 6. 服务端将 Web Server 的处理结果响应给客户端 18 | 7. 客户端接收服务端的响应,并渲染处理结果,呈现出来并断开 TCP 连接。 19 | 20 | 21 | ## 概念术语 22 | 23 | 1.**客户端** 24 | 25 | 客户端,是指与服务端相对应,为客户提供本地服务的程序。一般安装在普通的用户机上,需要与服务端互相配合运行。Web Application 的客户端一般是浏览器。 26 | 27 | 2.**服务端** 28 | 29 | 服务端是为客户端服务的,服务的内容诸如向客户端提供资源,保存客户端数据。 30 | 31 | 3.**三次握手** 32 | 33 | 三次握手,又名询问握手协议,是一个用来验证用户或网络提供者的协议。 34 | 35 | 4.**CGI** 36 | 37 | CGI 全称是 Common Gateway Interface,CGI 是外部应用程序与 Web Server 之间的接口标准,是在 CGI 程序和 Web 服务器之间传递信息的规程。CGI 规范允许 Web Server 执行外部程序,并将它们的输出发送给Web浏览器。 38 | 39 | 5.**DNS** 40 | 41 | DNS 全称是 Domain Name System,译为域名系统。它可以将域名和 IP 地址互相映射,能够让用户使用域名就可以访问互联网服务。 42 | 43 | 6.**HTTP** 44 | 45 | HTTP 全称是 HyperText Transfer Protocol,译为超文本传输协议,是一种网络协议。 46 | 47 | 7.**Web Server** 48 | 49 | 通常指 Apache、Nginx 等服务器软件 50 | 51 | 52 | ##机制原理 53 | 54 | 首先从 HTTP 协议开始谈起,HTTP 协议工作流程是客户端发送一个请求给服务端,服务端在接收到这个请求后将产生一个响应返回给客户端。那么 HTTP 请求和 HTTP 响应是什么? 55 | 56 | ### HTTP 请求 57 | 当客户端向服务端发出请求时,它向服务端传递一个数据块,即请求信息,HTTP 请求信息由三部分组成: 58 | 59 | - **请求行** 60 | - **请求报头** 61 | - **请求正文** 62 | 63 | ### 请求行 64 | 65 | 请求行以一个方法符号开头,以空格分开,后面跟着请求的 URI 和协议的版本,格式如下: 66 | ```c 67 | Request Method - URI HTTP-Version CRLF 68 | ``` 69 | 上述格式中各参数说明如下: 70 | 71 | - **Method**:请求方法,请求方法有多种 72 | - PUT 73 | - GET 74 | - POST 75 | - HEAD 76 | - TRACE 77 | - DELETE 78 | - OPTIONS 79 | - CONNECT 80 | - **Request-URI**:一个统一资源标识符 81 | - **HTTP-Version**:请求的 HTTP 协议版本。 82 | - **CRLF**:回车和换行 83 | 84 | ### 请求报头 85 | 86 | 请求报头属于 HTTP 消息报头之一,请求报头允许客户端向服务端传递请求的附加信息以及客户端自身的信息。 87 | 每个报头域组成形式如下: 88 | ``` 89 | 名字 + : + 空格 + 值 90 | ``` 91 | 比较重要的几个报头如下: 92 | 93 | - **Host**:头域指定请求资源的 Internet 主机和端口号,必须表示请求 URL 的原始服务器或者网关的位置。 94 | - **Accept**:告诉服务器可以接受的文件格式。 95 | - **Cookie**:Cookie 份两种,一种是客户端向服务器发送的,使用 Cookie 报头,用来标记一些信息;另一种是服务器发送给浏览器的,报头为 Set-Cookie。 96 | - **Referer**:头域允许客户端指定请求 URI 的源资源地址,这可以允许服务器生成回退链表,可用来登录、优化缓存等。 97 | - **User-Agent**:UA 包含发出请求的用户信息。通常 User-Agent 包含浏览者的信息,主要是浏览器的名称版本和所用的操作系统。 98 | - **Connection**:表示是否需要持久连接。 99 | - **Cache-Control**:指定请求和响应遵循的缓存机制 100 | - **Content-Range**:响应的资源范围。可以在每次请求中标记请求的资源范围,在连接断开重连时,客户端只请求重连时,客户端只请求该资源未下载的部分,而不是重新请求整个资源。 101 | - **Content-Length**:内容长度 102 | - **Accept-Encoding**:指定所能接受的编码方式。通常服务器会对页面进行 GZIP 压缩后再输出以减少流量。一般浏览器均支持对这种压缩后的数据进行处理。 103 | 104 | ### 请求正文 105 | 106 | 请求头和请求正文之间的一个空行表示请求头已经结束了。请求正文中可以包含提交的查询字符串信息。GET 方式没有请求正文。 107 | ``` 108 | username=admin&password=123456 109 | ``` 110 | 111 | ### HTTP 请求示例 112 | ``` 113 | GET /menu HTTP/1.1 114 | Host: localhost:5000 115 | Connection: keep-alive 116 | Cache-Control: max-age=0 117 | Upgrade-Insecure-Requests: 1 118 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36 119 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 120 | Accept-Encoding: gzip, deflate, sdch 121 | Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 122 | Cookie: cartalyst_sentry=luisedware#anneason 123 | ``` 124 | 125 | 126 | ### HTTP 响应 127 | 服务端在接收和解释请求消息后,服务端会返回一个 HTTP 响应消息。HTTP 响应也由三个部分组成,分别是: 128 | 129 | - **响应行** 130 | - **响应报头** 131 | - **响应正文** 132 | 133 | ### 响应行 134 | 135 | 响应行格式如下: 136 | ``` 137 | HTTP - Version Status - Code Reason - Phrase CRLF 138 | ``` 139 | 140 | 上述格式中各参数说明如下: 141 | 142 | - **HTTP - Version**:服务端 HTTP 协议的版本。 143 | - **Status-Code**:服务端发回的响应状态代码。 144 | - 状态代码由三位数字组成,第一个数字定义了响应的类别,有五种可能取值: 145 | - 1xx:提示信息 - 请求已接收,继续处理。 146 | - 2xx:成功 - 请求已被成功接收、理解、接受。 147 | - 3xx:重定向 - 要完成请求必须进行更进一步的操作。 148 | - 4xx:客户端错误 - 请求有语法错误或请求无法实现。 149 | - 5xx:服务端错误 - 服务器未能实现合法的请求。 150 | - 常见状态代码、状态描述和说明如下: 151 | - 200 OK: 客户端请求成功 152 | - 400 Bad Request:客户端请求有语法错误,不能被服务器所理解 153 | - 401 Unauthorize:请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用 154 | - 403 Forbidden:服务器收到请求,但是拒绝提供服务。 155 | - 404 Not Found:请求资源不存在,例如输入了错误的 URL 156 | - 500 Internal Server Error:服务器发生了不可预期的错误。 157 | - 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常 158 | - **Reason-Phrase**:状态代码的文本描述。 159 | 160 | ### 响应报头 161 | 162 | 响应报头属于 HTTP 消息报头之一,响应报头允许服务器传递不能放在响应行中的附加响应信息,以及关于服务器的信息和对 Request-URI 所标识的资源进行下一步的访问的信息(如 Location) 163 | 164 | ### 响应正文 165 | 响应正文就是服务端返回的资源内容,响应报头和正文之间也必须使用空行分隔。 166 | 167 | ### HTTP 响应示例 168 | ``` 169 | HTTP/1.1 200 OK 170 | host: localhost:8000 171 | connection: close 172 | x-powered-by: PHP/7.0.5 173 | cache-control: no-cache 174 | date: Fri, 10 Jun 2016 09:29:07 GMT 175 | content-type: text/html; charset=UTF-8 176 | set-cookie: XSRF-TOKEN=eyJpdiI6IlwvKzBIXC9JNzN2YUtpNGNaNndyTG40dz09IiwidmFsdWUiOiJtZlBDOEFQSmNUdzkyMXNQV1NSN2hPVjVFK1FHXC8xVU9nVGMwWFdcL3Q4MWlzc1A0dnhvRlBQckw1YVNpV0hQSTdFODBOQ2FFanF6YVg1TlRiQUhleEtnPT0iLCJtYWMiOiI5ZGRkOWEyYzk3OWY2YzZhOTA5MmFiOTA5ZmFmZmRiNTYxMzA5MjQ4NDdjYjcyNzIzNThjMzAxNmRjNDkzN2UxIn0%3D; expires=Fri, 10-Jun-2016 11:29:07 GMT; Max-Age=7200; path=/ 177 | set-cookie: laravel_session=eyJpdiI6ImdZWUZNOGFKTzV6YWpcL1lPRzRDTHdnPT0iLCJ2YWx1ZSI6IjFxRGRUOG5jRGVSdWJLXC9CMTl6MVJFdVlmaVpER04xb0piMWp3Q3JcL3dMZmR6b3UrU3lpb3FzQTR5QlpoTWxlOE92cmVCbW5iZGZwUUZ1a0d3ZjQrVUE9PSIsIm1hYyI6ImQ5N2M2MjVmYjcxNmE5YzgyY2IyZWJhODhiZTA0NGUxNTdlYmZhYjBkOGEzYzdiNTBiYmJjODE3MWJiOTA5NTIifQ%3D%3D; expires=Fri, 10-Jun-2016 11:29:07 GMT; Max-Age=7200; path=/; HttpOnly 178 | Transfer-Encoding: chunked 179 | 180 | 181 | 182 | HTTP响应示例<title> 183 | </head> 184 | <body> 185 | Hello World! 186 | </body> 187 | </html> 188 | ``` 189 | 190 | ## Apache + PHP/Nginx + FPM + PHP 的工作原理 191 | 当服务端接收 HTTP 请求后,服务端的服务器软件,如Apache、Nginx则会根据配置处理 HTTP 请求进行分发处理。如果客户端请求的是 index.html 文件,那么 Web Server 会根据配置文件去对应目录下找到这个文件,然后让服务端发送给客户端,这样分发的是静态资源。如果客户端请求的是 index.php 文件,根据配置文件,Web Server 知道这个不是静态资源,需要去找 PHP 解析器来处理,那么 Web Server 会把这个请求简单处理,然后交给 PHP 解析器。 192 | 193 | 当 Web Server 收到 index.php 这个请求后,会启动对应的 CGI 程序,CGI 程序就是 PHP 的解析器了。PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以 CGI 规定的格式返回处理结果,Web Server 再把结果返回给客户端。实现 PHP 解析器的方法有三种: 194 | 195 | - **Module**:PHP Module加载方法。 196 | - **PHP-CGI**:是 PHP 对 Web Server 提供的 CGI 协议的接口程序。 197 | - **PHP-FPM**:是 PHP 对 Web Server 提供的 FastCGI 协议的接口程序,额外提供了相对智能的任务管理。 198 | 199 | 以上三种实现方法都是本质都是通过 PHP 的 SAPI 层调用 PHP 执行的。 200 | 201 | 202 | 203 | ### Module 204 | 在了解 CGI 之前,我们先了解一下 Apache 处理 PHP 的一种常见方法:PHP Module 加载方式。在 Apache 的配置文件 httpd.conf,我们可以查找到以下语句: 205 | 206 | ```html 207 | LoadModule php7_module /usr/local/opt/php70/libexec/apache2/libphp7.so 208 | 209 | <FilesMatch .php$> 210 | SetHandler application/x-httpd-php 211 | </FilesMatch> 212 | 213 | <IfModule dir_module> 214 | DirectoryIndex index.php index.html 215 | </IfModule> 216 | ``` 217 | 218 | 这种方法,本质上是使用 Apache 的 `LoadModule` 来加载 `php7_module`,也就是把 PHP 作为 Apache 一个子模块来运行。当客户端请求 PHP 文件时,`Apache` 就会调用 `php7_module` 来解析 PHP 代码。 219 | 220 | 这种模式将 PHP 模块安装到 Apache 中,每一次 Apache 接收请求时,都会产生一条进程,这条进程就完整的包括 PHP 的运算计算,数据读取等各种操作。 221 | 222 | 由于每次请求都需要产生一条进程来连接 SAPI 来完成请求,一旦并发数过高,服务端就会承受不住。而且把 php7_module 加载进 Apache 中,出现问题时很难定位是 PHP 的问题 还是 Apache 的问题。 223 | 224 | 225 | ### PHP-CGI 226 | PHP-CGI 是使用 PHP 实现 CGI 接口的程序。当 Web Server 接收到 PHP 文件请求时,会分发给 CGI 程序处理,CGI 程序处理完毕后,会返回结果给 Web Server,Web Server 再返回给客户端。 227 | 228 | PHP-CGI 的好处就是完全独立于 Web Server,只是作为中间层,提供接口给 Apache 和 PHP,它们通过 CGI 来完成数据传递。这样做的好处就减少了两者之间的关联,出现错误时能够较好地定位。 229 | 230 | 但是 PHP-CGI 采用的是 fork-and-execute 模式,就是每次请求都会有进程启动和退出的过程,在高并发下,Web Server 容易奔溃。 231 | 232 | ### PHP-FPM 233 | 在了解 PHP-FPM 之前,我们先来了解一下 FastCGI。 234 | 235 | 从根本上来说,FastCGI 是用来提高 CGI 程序性能的。类似于 CGI,FastCGI 也可以说是一种协议。 236 | 237 | FastCGI 像是一个常驻(long-live)型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 fork 一次。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行,并且接受来自其它网站服务器来的请求。 238 | 239 | FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 解释器进程保持在内存中,并因此获得较高的性能。众所周知,CGI 解释器的反复加载是 CGI 性能低下的主要原因,如果 CGI 解释器保持在内存中,并接受 FastCGI 进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over 特性等等。 240 | 241 | FastCGI 接口方式采用C/S结构,可以将 Web Server 和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给 FastCGI 进程来执行,然后将得到的结果返回给浏览器。这种方式可以让 Web Server 专一地处理静态请求,或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 242 | 243 | PHP-FPM 是对 FastCGI 协议的具体实现,它负责管理一个进程池,来处理 Web Server 的请求。它自身并不直接处理请求,它会交给 PHP-CGI 去进行处理。因为 PHP-CGI 只是一个 CGI 程序,它只会解析请求,返回结果,不会管理进程。PHP-FPM 是用于调度管理 PHP 解释器 PHPCGI 的管理程序。 244 | 245 | ### CGI、FastCGI、PHP-CGI、PHP-FPM 的区别 246 | 247 | - **CGI**:是一种协议,是外部应用程序与 Web Server 之间的接口标准,是在 CGI 程序和 Web 服务器之间传递信息的规程。 248 | - **FastCGI**:是一种协议,通过守护进程来管理 CGI 程序,将 CGI 程序常驻于内存中,不必每次处理请求重新初始化 php.ini 和其他数据,提高 CGI 程序的性能。其本身并不处理 PHP 文件,只是负责进程的调度。 249 | - **PHP-CGI**:是使用 PHP 实现 CGI 协议的程序,用于解析和处理 PHP 脚本。 250 | - **PHP-FPM**:是一个只用于 PHP 的进程管理器,提供更好的 PHP 进程管理方式,可以有效控制进程,平滑地加载 PHP 配置文件。 251 | 252 | ### Apache 常用的配置与模块 253 | 254 | - **Options**(配置选项) 255 | - **Indexes**:目录浏览,是否允许在目录下没有 index.html,index.php 的时候显示目录。 256 | - **Multiviews**:文件匹配,服务器执行一个隐含的文件名模式匹配,并在其结果中选择。 257 | - **FollowSymLinks**:符号链接,是否允许通过符号链接跨越 DocumentRoot 访问其他目录。 258 | - **AllowOverride**(是否允许根目录下的 `.htaccess` 文件起到 `URL rewrite` 的作用) 259 | - **All** 260 | - **None** 261 | - **LoadModule**(加载模块) 262 | - **Listen**(Apache 默认监听端口) 263 | - **ServerRoot**:Apache 安装的基础目录 264 | - **DocumentRoot**:Apache 缺省文件根目录 265 | - **DirectoryIndex**:网站默认首页文件 266 | - **LogLevel**:日志级别设置 267 | - **ErrorLog**:错误日志存放路径 268 | - **CustomLog**:访问日志存放路径 269 | - **VirutalHost**(虚拟主机与配置参数) 270 | - **ServerName**:虚拟域名 271 | - **ServerAlias**:虚拟域名的别名 272 | - **ServerAdmin**:服务器管理员邮箱 273 | - **DocumentRoot**:项目代码根目录 274 | - **LogLevel**:日志级别设置 275 | - **ErrorLog**:错误日志存放路径 276 | - **CustomLog**:访问日志存放路径 277 | - **MPM**(工作模式/多处理模块) 278 | - **Prefork** 279 | - **StartServers**:服务器启动时默认开启的进程数 280 | - **MinSpareServers**:最小的空闲进程数 281 | - **MaxSpareServers**:最大的空闲进程数 282 | - **ServerLimit**:在Apache的生命周期内,限制MaxClients的最大值 283 | - **MaxClients**:最大的并发请求数,最大值不能超过 ServerLimit 设置的值 284 | - **MaxRequestsPerChild**:一个进程可以处理的最多的请求数(进程复用),如请求超过该设置则杀死进程,0表示永不过期。 285 | - **Worker** 286 | - **Event** 287 | - **Order, Deny, Allow**(认证,授权与访问控制) 288 | - **HostnameLookups off**(避免 DNS 查询) 289 | - **Timeout**(请求超时) 290 | - **Include**(文件包含) 291 | - **Proxy、mod_proxy**(代理) 292 | - **Cache Guide**(缓存) 293 | 294 | ### Nginx 常用模块与配置指令上下文 295 | 296 | #### Nginx 模块 297 | 298 | Nginx 的模块从功能角度主要可以分为以下三类: 299 | 300 | - **Handler** 模块:主要负责处理客户端请求并产生待响应内容,比如 `ngx_http_static_module`模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。 301 | - **Filter** 模块:主要负责对输出的内容进行处理,可以对输出进行修改,如 `ngx_http_not_modified_filter_module`,`ngx_http_header_filter_module` 模块。 302 | - **Upstream** 模块:实现反向代理的功能,将真正的请求转发到后端服务器上,如 `ngx_http_proxy_module`、`ngx_http_fastcgi_module` 模块。 303 | 304 | 305 | #### Nginx 配置指令 306 | 307 | - **main**(Nginx 在运行时与具体业务功能无关的一些参数,比如工作进程数,运行的身份等) 308 | - **user** 309 | - **worker_processes** 310 | - **error_log** 311 | - **events** 312 | - **http** 313 | - **mall** 314 | - **events**(定义 Nginx 事件工作模式与连接数上限等参数) 315 | - **use** [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; 316 | - **worker_connections** 317 | - **http**(与提供 HTTP 服务相关的一些配置参数,如是否使用 keepalive、是否使用 gzip 进行压缩等) 318 | - **server** 319 | - **server**(http服务上支持若干虚拟主机。每个虚拟主机一个对应的server配置项,配置项里面包含该虚拟主机相关的配置。在提供mail服务的代理时,也可以建立若干server.每个server通过监听的地址来区分) 320 | - **listen** 321 | - **server_name** 322 | - **access_log** 323 | - **location** 324 | - **protocol** 325 | - **proxy** 326 | - **smtp_auth** 327 | - **xclient** 328 | - **location**(http服务中,某些特定的URL对应的一系列配置项。) 329 | - **root** 330 | - **index** 331 | - **mail**(实现email相关的SMTP/IMAP/POP3代理时,共享的一些配置项(因为可能实现多个代理,工作在多个监听地址上) 332 | - **server** 333 | - **auth_http** 334 | - **imap_capabilities** 335 | 336 | -------------------------------------------------------------------------------- /博客文章/PHP-Codebook-一.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Codebook(一) 3 | date: 2017-01-22 06:06:56 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 1.1 访问子串 9 | 10 | #### 问题 11 | 想知道一个字符串是否包含一个特定的子串。例如,想查看一个 email 地址是否包含一个 @ 12 | 13 | #### 实现 14 | ```php 15 | <?php 16 | $string = '506510463@qq.com'; 17 | 18 | if (($length = strpos($string, '@')) === false) { 19 | echo 'There was no @ in the e-mail address'; 20 | } else { 21 | echo 'This is a right e-mail address-' . $length; 22 | } 23 | ``` 24 | strpos 找到子串时,会返回子串的位置。找不到子串时,会返回 false,为了区分 0 和 false,必须使用 恒等操作符或非恒等操作符来进行判断。 25 | 26 | ## 1.2 抽取子串 27 | 28 | #### 问题 29 | 30 | 希望从字符串中的某个特定位置开始抽取这个字符串的一部分。例如,对于输入到一个表单的用户名,想要得到这个用户名的前八位字符。 31 | 32 | #### 实现 33 | ```php 34 | <?php 35 | $start = 0; 36 | $length = 8; 37 | $string = "LuisEdware"; 38 | 39 | echo substr($string, $start, $length) . "\n"; 40 | // LuisEdwa 41 | 42 | // 如果忽略 $length,substr() 会返回从 $start 到原字符串末尾的子串 43 | $start = 5; 44 | echo substr($string, $start) . "\n"; 45 | // dware 46 | 47 | // 如果 $start 大于字符串的长度,substr() 将返回 false 48 | $start = 20; 49 | var_dump(substr($string, $start)); 50 | // false 51 | 52 | // 如果 $start 加 $length 超过了字符串末尾,substr() 将返回从 $start 开始至字符串末尾的所有字符 53 | echo substr($string, 8, 5) . "\n"; 54 | // re 55 | 56 | // 如果 $start 为负数,substr() 会从字符串末尾倒数来确定子串从哪里开始 57 | echo substr($string, -6) . "\n"; 58 | // Edware 59 | echo substr($string, -6, 2) . "\n"; 60 | // Ed 61 | 62 | // 如果 $length 为负数,substr() 会从字符串末尾倒数来确定子串到哪里结束 63 | echo substr($string, 2, -2) . "\n"; 64 | // isEdwa 65 | echo substr($string, -4, -2) . "\n"; 66 | // wa 67 | ``` 68 | 69 | ## 1.3 替换子串 70 | 71 | #### 问题 72 | 73 | 希望用另外一个不同的字符串来替换一个子串。例如,打印一个信用卡号之前,想要对除了后四位以外的部分模糊处理。或者当需要显示的文档过大,无法一次全部显示,可能希望显示一部分文本,另外还有一个链接指向其余的文本。 74 | 75 | #### 实现 76 | ```php 77 | <?php 78 | $creditCard = "6217 0032 4000 0600 075"; 79 | echo substr_replace($creditCard, '****', 0, strlen($creditCard) - 4) . "\n"; 80 | // **** 075 81 | 82 | echo substr_replace($creditCard, '**** **** **** ****', 0, strlen($creditCard) - 4) . "\n"; 83 | // **** **** **** **** 075 84 | 85 | $text = "Chief Justice Roberts, President Carter, President Clinton, President Bush, President Obama, fellow Americans and people of the world, thank you. 86 | 87 | We, the citizens of America, are now joined in a great national effort to rebuild our country and restore its promise for all of our people."; 88 | 89 | echo substr_replace($text, "...", 25); 90 | ``` 91 | 92 | ## 1.4 逐字节处理字符串 93 | 94 | #### 问题 95 | 96 | 需要分别处理字符串中的各个字节 97 | 98 | #### 实现 99 | ```php 100 | <?php 101 | 102 | $string = "This weekend,I am going shopping for pet chicken."; 103 | $vowels = 0; 104 | 105 | for ($i = 0, $j = strlen($string); $i < $j; $i++) { 106 | if (strstr('aeiouAEIOU', $string[$i])) { 107 | $vowels++; 108 | $string[$i] = ucwords($string[$i]); 109 | } 110 | } 111 | 112 | echo $vowels . "\n"; 113 | echo $string; 114 | ``` 115 | 116 | ## 1.5 按单词或字节反转字符串 117 | 118 | #### 问题 119 | 120 | 希望反转一个字符串中的单词或字节 121 | 122 | #### 解决方案 123 | ```php 124 | <?php 125 | 126 | $string = "This is not a palindrome"; 127 | 128 | // 按字节反转 129 | echo strrev($string) . "\n"; 130 | // emordnilap a ton si sihT 131 | 132 | // 按单词反转 133 | $string = "Hello World,My Name is Luis Edware."; 134 | 135 | // 将字符串分解为单词 136 | $words = explode(' ', $string); 137 | 138 | // 反转单词数组 139 | $words = array_reverse($words); 140 | 141 | // 重新构建字符串 142 | $string = implode(' ', $words); 143 | echo $string; 144 | // Edware. Luis is Name World,My Hello 145 | ``` 146 | 147 | ## 1.6 生成随机字符串 148 | 149 | #### 问题 150 | 151 | 希望生成一个随机字符串 152 | 153 | #### 实现 154 | ```php 155 | <?php 156 | function str_rand($length, $characters = null) 157 | { 158 | if ($characters === null) { 159 | $characters = '0123456789qwertyuiopasdfghjklzxcvbnm!@#$%^&*()-=_+[]{}:";,.<>/?'; 160 | } 161 | 162 | if (!is_int($length) || $length < 0) { 163 | return false; 164 | } 165 | 166 | $charactersLength = strlen($characters); 167 | $string = ''; 168 | 169 | for ($i = $length; $i > 0; $i--) { 170 | $string .= $characters[mt_rand(0, $charactersLength)]; 171 | } 172 | 173 | return $string; 174 | } 175 | 176 | echo str_rand(32); 177 | ``` 178 | 179 | ## 1.8 控制大小写 180 | 181 | #### 问题 182 | 183 | 需要将字符串中的字母全大写或全小写,或者改变字母的大小写。例如,希望名字中的首字母大写,而其余字母都为小写。 184 | 185 | #### 实现 186 | ```php 187 | <?php 188 | // 使用 ucfirst() 或 ucwords() 将一个单词或多个单词中的首字母大写 189 | $string = "my name is Luis edware"; 190 | echo ucfirst($string) . "\n"; 191 | echo ucwords($string) . "\n"; 192 | 193 | // strtoupper()、strtolower() 将作用整个字符串 194 | echo strtoupper($string) . "\n"; 195 | echo strtolower($string); 196 | ``` 197 | 198 | ## 1.10 去除字符串首尾的空格 199 | 200 | #### 问题 201 | 202 | 希望从字符串开头和末尾删除空白符。例如,在验证用户输入之前,可能希望先完成清理。 203 | 204 | #### 实现 205 | ```php 206 | <?php 207 | /** 208 | * ltrim() 函数从字符串开头删除空白符 209 | * rtrim() 函数从字符串末尾删除空白符 210 | * trim() 函数则删除字符串开头和末尾的空白符 211 | */ 212 | 213 | $lString = " LuisEdware"; 214 | $rString = "LuisEdware "; 215 | $string = " LuisEdware "; 216 | 217 | echo ltrim($lString) . "\n"; 218 | echo rtrim($rString) . "\n"; 219 | echo trim($string) . "\n"; 220 | 221 | // trim() 函数还可以从字符串中删除用户指定的字符。 222 | echo ltrim('506510463 2794408425 Luis Edware', '0..9') . "\n"; 223 | echo rtrim('LuisEdware 5065104632794408425', '0..9') . "\n"; 224 | ``` 225 | 226 | ## 1.13 生成固定宽度字段数据记录 227 | 228 | #### 问题 229 | 230 | 需要格式化数据记录,使得每个字段占据指定数目的字符。 231 | 232 | #### 实现 233 | ```php 234 | <?php 235 | 236 | $books = [ 237 | ['trueName', 'nickName', 'birthday'], 238 | ['Elmer Gantry', 'Sinclair Lewis', 1927], 239 | ['The Scarlatti Inheritance', 'Robert Ludlum', 1971], 240 | ['The Parsifal Mosaic', 'William Styron', 1979], 241 | ]; 242 | 243 | foreach ($books as $book) { 244 | print pack('A30A20A10', $book[0], $book[1], $book[2]) . "\n"; 245 | } 246 | /* 247 | trueName nickName birthday 248 | Elmer Gantry Sinclair Lewis 1927 249 | The Scarlatti Inheritance Robert Ludlum 1971 250 | The Parsifal Mosaic William Styron 1979 251 | */ 252 | ``` 253 | 254 | ## 1.15 分解字符串 255 | 256 | #### 问题 257 | 258 | 需要将一个字符串分解为片段。例如希望访问用户在一个 <textarea\> 表单域中输入的各行文本 259 | ```php 260 | <?php 261 | $string = 'My sentence is not very complicated'; 262 | $words = explode(' ', $string); 263 | /* 264 | Array 265 | ( 266 | [0] => My 267 | [1] => sentence 268 | [2] => is 269 | [3] => not 270 | [4] => very 271 | [5] => complicated 272 | ) 273 | */ 274 | ``` 275 | 276 | ## 1.16 使文本在指定行长度自动换行 277 | 278 | #### 问题 279 | 280 | 需要实现字符串自动换行 281 | 282 | #### 实现 283 | ```php 284 | <?php 285 | $text = "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum."; 286 | 287 | echo '<pre>' . $text . '</pre>' . "\n"; 288 | echo wordwrap($text, 50); 289 | ``` 290 | 291 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /博客文章/PHP-Codebook-九.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Codebook(九) 3 | date: 2017-01-24 11:15:01 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 9.1 处理表单输入 9 | 10 | #### 问题 11 | 12 | 希望使用一个 HTML 页面提交表单,然后在同一个页面中处理这个表单中输入的数据 13 | 14 | #### 实现 15 | 16 | 使用 $_SERVER['REQUEST_METHOD'] 变量来确定请求是用 get 还是 post 方法提交的。 17 | 18 | ## 9.2 验证表单输入:必填域 19 | 20 | #### 问题 21 | 22 | 希望确保必须为一个表单元素提供一个值。例如,希望保证一个文本框不为空 23 | 24 | #### 实现 25 | 26 | ```php 27 | <?php 28 | if (!(filter_has_var(INPUT_GET, 'flavor') 29 | && (strlen(filter_input(INPUT_GET, 'flavor')))) 30 | ) { 31 | print "You must enter your favorite ice cream flavor.\n"; 32 | } else { 33 | echo $_GET['flavor']; 34 | } 35 | 36 | if ((filter_has_var(INPUT_GET, 'color')) 37 | && (strlen(filter_input(INPUT_GET, 'color', FILTER_SANITIZE_STRING)) <= 5) 38 | ) { 39 | print "Color must be more than 5 characters."; 40 | } 41 | 42 | if (!(filter_has_var(INPUT_GET, 'choices')) 43 | && filter_input(INPUT_GET, 'choices', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY) 44 | ) { 45 | print "You must select some choices.\n"; 46 | } 47 | 48 | ?> 49 | ``` 50 | 51 | ## 9.3 验证表单输入:数字 52 | 53 | #### 问题 54 | 55 | 希望确保在一个表单输入框中输入了一个数。 56 | 57 | #### 实现 58 | ```php 59 | <?php 60 | $age = filter_input(INPUT_GET, 'age', FILTER_VALIDATE_INT); 61 | if ($age === false) { 62 | print "Submitted age is invalid."; 63 | } 64 | 65 | $price = filter_input(INPUT_GET, 'price', FILTER_VALIDATE_FLOAT); 66 | if ($price === false) { 67 | print "Submitted price is invalid."; 68 | } 69 | ``` 70 | 71 | ## 9.4 验证表单输入:email 地址 72 | 73 | #### 问题 74 | 75 | 希望知道用户提供的一个 email 地址是否合法 76 | 77 | #### 实现 78 | ```php 79 | <?php 80 | $email = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL); 81 | if ($email === false) { 82 | print "Submitted email is invalid."; 83 | } 84 | ``` 85 | 86 | ## 9.16 处理变量名中包含点号的远程变量 87 | 88 | #### 问题 89 | 90 | 希望处理一个变量,变量名中有一个点号,不过提交表单时,无法在 $_GET 或者 $_POST 中找到这个变量。 91 | 92 | 93 | #### 实现 94 | 95 | 将变量名中的点号替换为一个下划线。 96 | -------------------------------------------------------------------------------- /博客文章/PHP-Codebook-二.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Codebook(二) 3 | date: 2017-01-22 11:38:37 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 2.1 检查变量是否包含一个合法数字 9 | 10 | #### 问题 11 | 12 | 希望确保变量包含一个数(即使变量的类型是字符串),或者希望检查变量不仅是一个数,而且要特别指定其类型是一个数字类型。 13 | 14 | #### 实现 15 | 16 | ```php 17 | <?php 18 | foreach ([5,'5','05',12.3,'16.7','five',oxDECAFBAD,'10e2000'] as $maybeNumber) { 19 | $isItNumeric = is_numeric($maybeNumber); 20 | $actualType = gettype($maybeNumber); 21 | $string = "Is the $actualType $maybeNumber numeric?"; 22 | 23 | if(is_numeric($maybeNumber)){ 24 | $answer = "yes"; 25 | }else{ 26 | $answer = "no"; 27 | } 28 | 29 | echo pack("A40A3",$string,$answer)."\n"; 30 | } 31 | ?> 32 | /* 33 | Is the integer 5 numeric? yes 34 | Is the string 5 numeric? yes 35 | Is the string 05 numeric? yes 36 | Is the double 12.3 numeric? yes 37 | Is the string 16.7 numeric? yes 38 | Is the string five numeric? no 39 | Is the string oxDECAFBAD numeric? no 40 | Is the string 10e2000 numeric? yes 41 | */ 42 | ``` 43 | 44 | ## 2.2 比较浮点数 45 | 46 | 47 | #### 问题 48 | 49 | 希望检查两个浮点数是否相等 50 | 51 | #### 实现 52 | ```php 53 | <?php 54 | $delta = 0.00001; 55 | $a = 1.00000001; 56 | $b = 1.00000000; 57 | 58 | if(abs($a - $b) < $delta){ 59 | print "$a and $b are equal enough"; 60 | } 61 | ``` 62 | 63 | ## 2.3 浮点数舍入 64 | 65 | #### 问题 66 | 67 | 希望舍入一个浮点数,可能取整为一个整数,或者舍入为指定的小数位数 68 | 69 | #### 实现 70 | 71 | ```php 72 | <?php 73 | 74 | // 四舍五入 round() 75 | echo round(2.4) . "\n"; 76 | echo round(2.5) . "\n"; 77 | 78 | // 向上取整 79 | echo ceil(2.4) . "\n"; 80 | 81 | // 向下取整 82 | echo floor(2.9) . "\n"; 83 | 84 | // 一个数正好落在两个整数中间的位置,PHP 会向远离 0 的方向取整 85 | echo round(2.5) . "\n"; 86 | echo round(-2.5) . "\n"; 87 | echo round(2.4) . "\n"; 88 | echo round(-2.4) . "\n"; 89 | 90 | echo ceil(2.5) . "\n"; 91 | echo ceil(-2.5) . "\n"; 92 | echo ceil(2.4) . "\n"; 93 | echo ceil(-2.4) . "\n"; 94 | 95 | echo floor(2.5) . "\n"; 96 | echo floor(-2.5) . "\n"; 97 | echo floor(2.4) . "\n"; 98 | echo floor(-2.4) . "\n"; 99 | ``` 100 | 101 | ## 2.4 处理一系列整数 102 | 103 | #### 问题 104 | 105 | 希望对一个整数范围应用一段代码 106 | 107 | #### 实现 108 | ```php 109 | // 使用 range() 函数实现 110 | <?php 111 | foreach (range(0, 100, 5) as $number) { 112 | echo $number . "\n"; 113 | printf("%d squared is %d\n", $number, $number * $number); 114 | printf("%d cubed is %d\n", $number, $number * $number * $number); 115 | } 116 | ``` 117 | 118 | ## 2.5 在指定范围内生成随机数 119 | 120 | #### 问题 121 | 122 | 希望在指定的数字范围内生成一个随机数 123 | 124 | #### 实现 125 | ```php 126 | <?php 127 | $lower = 0; 128 | $upper = 1024; 129 | $randNumber = mt_rand($lower, $upper); 130 | 131 | echo $randNumber; 132 | ``` 133 | 134 | ## 2.6 生成可预测的随机数 135 | 136 | #### 问题 137 | 138 | 希望生成可预测的随机数,从而可以保证可重复的行为 139 | 140 | #### 实现 141 | ```php 142 | <?php 143 | 144 | function pick_color() 145 | { 146 | $colors = ['red', 'orange', 'yellow', 'blue', 'green', 'indigo', 'violet']; 147 | $i = mt_rand(0, count($colors) - 1); 148 | 149 | return $colors[$i]; 150 | } 151 | 152 | mt_srand(34534); 153 | $first = pick_color(); 154 | $second = pick_color(); 155 | 156 | print "$first is red and $second is yellow"; 157 | ``` 158 | 159 | ## 2.10 格式化数字 160 | 161 | #### 问题 162 | 163 | 希望输出一个数,要包括千分位分隔符和小数点。 164 | 165 | #### 实现 166 | ```php 167 | <?php 168 | $number = 1234.56; 169 | // 千位分隔符 170 | echo number_format($number) . "\n"; 171 | 172 | // 第二个参数指定使用了小数位数 173 | echo number_format($number, 2) . "\n"; 174 | 175 | // 第三个参数指定小数点字符,第四个参数指定千位分隔符 176 | echo number_format($number, 2, "@", '#') . "\n"; 177 | ``` 178 | 179 | ## 2.16 转换进制 180 | 181 | #### 问题 182 | 183 | 需要将一个数从一个进制转换为另一个进制 184 | 185 | #### 实现 186 | ```php 187 | <?php 188 | // 十六进制 189 | $hex = 'a1'; 190 | 191 | // 从十六进制转换为十进制 192 | $decimal = base_convert($hex, 16, 10) . "\n"; 193 | echo $decimal; 194 | 195 | $decimal = 1024; 196 | echo base_convert($decimal, 10, 2) . "\n"; 197 | echo base_convert($decimal, 10, 8) . "\n"; 198 | echo base_convert($decimal, 10, 16); 199 | ``` 200 | -------------------------------------------------------------------------------- /博客文章/PHP-Codebook-六.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Codebook(六) 3 | date: 2017-01-24 08:34:59 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 6.4 使用命名参数 9 | 10 | #### 问题 11 | 12 | 希望按名为函数指定参数,而不是通过函数调用时的位置来指定。 13 | 14 | #### 实现 15 | ```php 16 | <?php 17 | function image1($params) 18 | { 19 | if (!isset($params['src'])) { 20 | $image['src'] = 'cow.png'; 21 | } 22 | 23 | if (!isset($params['alt'])) { 24 | $image['alt'] = 'Milk Factory'; 25 | } 26 | 27 | if (!isset($params['height'])) { 28 | $image['height'] = '100'; 29 | } 30 | 31 | if (!isset($params['width'])) { 32 | $image['width'] = '50'; 33 | } 34 | 35 | return '<img src="' . $image['src'] . '" alt="' . $image['alt'] . '" width="' . $image['width'] . '" height="' . $image['height'] . '">'; 36 | } 37 | 38 | function image2($params) 39 | { 40 | $defaults = [ 41 | 'src' => 'cow.png', 42 | 'alt' => 'milk factory', 43 | 'width' => 100, 44 | 'height' => 50, 45 | ]; 46 | 47 | $params = array_merge($defaults, $params); 48 | return '<img src="' . $image['src'] . '" alt="' . $image['alt'] . '" width="' . $image['width'] . '" height="' . $image['height'] . '">'; 49 | } 50 | ?> 51 | ``` 52 | 53 | ## 6.6 创建参数个数可变的函数 54 | 55 | #### 问题 56 | 57 | 希望定义一个参数个数可变的函数 58 | 59 | #### 实现 60 | ```php 61 | <?php 62 | 63 | function mean($numbers) 64 | { 65 | $sum = 0; 66 | 67 | $size = count($numbers); 68 | 69 | for ($i = 0; $i < $size; $i++) { 70 | $sum += $numbers[$i]; 71 | } 72 | 73 | $average = $sum / $size; 74 | 75 | return $average; 76 | } 77 | 78 | echo mean([96, 93, 98, 98]) . "\n"; 79 | 80 | function mean1() 81 | { 82 | $sum = 0; 83 | 84 | $size = func_num_args(); 85 | 86 | for ($i = 0; $i < $size; $i++) { 87 | $sum += func_get_arg($i); 88 | } 89 | 90 | $average = $sum / $size; 91 | 92 | return $average; 93 | 94 | } 95 | 96 | echo mean1(96, 93, 98, 98) . "\n"; 97 | 98 | function mean2() 99 | { 100 | $sum = 0; 101 | 102 | $size = func_num_args(); 103 | 104 | foreach (func_get_args() as $arg) { 105 | $sum += $arg; 106 | } 107 | $average = $sum / $size; 108 | 109 | return $average; 110 | } 111 | 112 | echo mean2(96, 93, 98, 98) . "\n"; 113 | ``` 114 | 115 | ## 6.7 按引用返回值 116 | 117 | #### 问题 118 | 119 | 希望按引用返回一个值,而不是按值返回。这样就无需为变量建立一个重复的副本。 120 | 121 | #### 实现 122 | ```php 123 | <?php 124 | 125 | function &array_find_value($needle, &$haystack) 126 | { 127 | foreach ($haystack as $key => $value) { 128 | if ($needle == $value) { 129 | return $haystack[$key]; 130 | } 131 | } 132 | } 133 | 134 | $names = ['Ann Eason', 'Luis Edware', 'Ivan Tomic', 'RouniFul']; 135 | 136 | $prince =& array_find_value('Ann Eason', $names); 137 | $prince = "梁非凡"; 138 | print_r($names); 139 | ``` 140 | 141 | ## 6.8 返回多个值 142 | 143 | #### 问题 144 | 145 | 希望从函数返回多个值。 146 | 147 | #### 实现 148 | ```php 149 | <?php 150 | 151 | function array_stats($values) 152 | { 153 | $min = min($values); 154 | $max = max($values); 155 | $mean = array_sum($values) / count($values); 156 | 157 | return [$min, $max, $mean]; 158 | } 159 | 160 | $values = range(1, 100); 161 | list($min, $max, $mean) = array_stats($values); 162 | 163 | echo sprintf("min is %d,max is %d,mean is %d", $min, $max, $mean); 164 | ``` 165 | 166 | ## 6.10 返回失败 167 | 168 | #### 问题 169 | 170 | 希望从函数中指示失败 171 | 172 | #### 实现 173 | ```php 174 | <?php 175 | 176 | function lookup($name) 177 | { 178 | if (empty($name)) { 179 | return false; 180 | } 181 | } 182 | 183 | $name = false; 184 | if (false !== lookup($name)) { 185 | 186 | } 187 | ``` 188 | 189 | ## 6.11 调用可变函数 190 | 191 | 192 | #### 问题 193 | 194 | 希望根据一个变量的值来调用不同的函数 195 | 196 | #### 实现 197 | ```php 198 | <?php 199 | 200 | function get_file($fileName) 201 | { 202 | return file_get_contents($fileName); 203 | } 204 | 205 | $function = 'get_file'; 206 | $fileName = 'graphic.png'; 207 | 208 | call_user_func($function, $fileName); 209 | ``` 210 | -------------------------------------------------------------------------------- /博客文章/PHP-Codebook-四.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP Codebook(四) 3 | date: 2017-01-22 14:56:46 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | ## 4.1 指定并非从元素 0 开始的数组 9 | 10 | #### 问题 11 | 12 | 希望一步为一个数组赋多个元素,不过不希望第一个元素的索引为0 13 | 14 | 15 | #### 实现 16 | ```php 17 | <?php 18 | 19 | $presidents = [1 => 'Washington', 'Adams', 'Lincoln', 'Jefferson']; 20 | var_dump($presidents); 21 | 22 | $fruits[1] = 'apple'; 23 | $fruits[] = 'banana'; 24 | var_dump($fruits); 25 | ``` 26 | 27 | ## 4.3 数组初始化为一个整数范围 28 | 29 | #### 问题 30 | 31 | 希望将一系列连续的整数赋至一个数组 32 | 33 | #### 实现 34 | ```php 35 | <?php 36 | $cards = range(1,52); 37 | ``` 38 | 39 | ## 4.4 迭代处理数组 40 | ```php 41 | <?php 42 | $array = range(0, 12); 43 | 44 | foreach ($array as $item) { 45 | echo $item . "\n"; 46 | } 47 | 48 | foreach ($array as $key => $value) { 49 | echo $value . "\n"; 50 | } 51 | 52 | for ($key = 0, $size = count($array); $key < $size; $key++) { 53 | echo $array[$key] . "\n"; 54 | } 55 | 56 | // 将内部指针重置为数组起始位置 57 | reset($array); 58 | while (list($key, $value) = each($array)) { 59 | echo $value . "\n"; 60 | } 61 | 62 | array_map(function($value) { 63 | return $value; 64 | }, $array); 65 | ``` 66 | 67 | ## 4.5 从数组删除元素 68 | 69 | #### 问题 70 | 71 | 希望从一个数组删除一个或多个数组 72 | 73 | #### 实现 74 | ```php 75 | <?php 76 | $array = range(0, 12); 77 | 78 | // 删除元素 79 | unset($array[0]); 80 | 81 | // 删除多个连续的元素,array_splice 会自动对数组重新建立索引,避免留出空位。 82 | array_splice($array, 1, 9); 83 | var_dump($array); 84 | ``` 85 | 86 | 87 | ## 4.6 改变数组大小 88 | 89 | #### 问题 90 | 91 | 希望改变一个数组的大小,使它大于或小于目前的大小 92 | 93 | #### 实现 94 | ```php 95 | <?php 96 | 97 | // array_pad() 第一个 98 | $array = ['apple', 'banana', 'cocount']; 99 | $array = array_pad($array, 4, 'dates'); 100 | print_r($array); 101 | 102 | $array = array_pad($array, -10, 'zucchini'); 103 | print_r($array); 104 | ``` 105 | 106 | ## 4.7 将数组追加到另一个数组 107 | 108 | #### 问题 109 | 110 | 希望把两个数组合并为一个数组 111 | 112 | #### 实现 113 | ```php 114 | <?php 115 | // 合并只使用数值键的数组时,数组将重新编号,以保证值不会丢失。 116 | $array1 = ['hello', 'world']; 117 | $array2 = ['luis', 'edware', 'hello', 'world']; 118 | $array3 = array_merge($array1, $array2); 119 | print_r($array3); 120 | 121 | // + 操作符也可以用来合并数组 122 | print_r($array1 + $array2); 123 | 124 | // 倘若两个数组有重复的键,第二数组会覆盖这些重复键的值 125 | $array1 = ['A' => 'apple', 'B' => 'banana', 'O' => 'orange']; 126 | $array2 = ['A' => 'Luis', 'C' => 'Imooc']; 127 | print_r(array_merge($array1, $array2)); 128 | ``` 129 | 130 | ## 4.8 将数组转换为字符串 131 | 132 | #### 问题 133 | 134 | 希望将一个数组转换为一个格式化的字符串 135 | 136 | #### 实现 137 | ```php 138 | <?php 139 | $array = range(0, 9); 140 | $string1 = join(',', $array); 141 | $string2 = implode(',', $array); 142 | print_r($string1 . "\n"); 143 | print_r($string2 . "\n"); 144 | ``` 145 | 146 | ## 4.10 检查一个键是否存在数组中 147 | 148 | #### 问题 149 | 150 | 想知道一个数组是否包含某个键 151 | 152 | 153 | #### 实现 154 | ```php 155 | <?php 156 | 157 | $array = ['a' => 'apple', 'b' => 'banana', 'c' => 'cao ni ma']; 158 | if (array_key_exists('a', $array)) { 159 | echo $array['a'] . "\n"; 160 | } 161 | 162 | if (isset($array['a'])) { 163 | echo $array['c'] . "\n"; 164 | } 165 | ``` 166 | 167 | ## 4.11 检查一个元素是否在数组中 168 | 169 | #### 问题 170 | 171 | 希望知道一个数组是否包含某个值 172 | 173 | #### 实现 174 | ```php 175 | <?php 176 | 177 | $array = ['a' => 'apple', 'b' => 'banana', 'c' => 'cao ni ma']; 178 | 179 | if (in_array('apple', $array)) { 180 | echo 'Own it'; 181 | } else { 182 | echo 'Need it'; 183 | } 184 | 185 | $array = ['Emma', 'Pride and Prejudice', 'Northhanger Abbey']; 186 | $array = array_flip($array); 187 | if (array_key_exists('Emma', $array)) { 188 | echo 'Own it'; 189 | } else { 190 | echo 'Need it'; 191 | } 192 | ``` 193 | 194 | ## 4.12 查找一个值在数组中的位置 195 | 196 | #### 问题 197 | 198 | 希望知道一个值是否在数组中。如果这个值确实在数组中,希望知道它的键。 199 | 200 | #### 实现 201 | ```php 202 | <?php 203 | 204 | // 如果一个值在数组中多次出现,array_serach() 只能保证返回其中一个实例,而不一定是第一个实例。 205 | $position = ['a'=>'apple','banana','orange','b'=>'not bad']; 206 | if(($result = array_search('123', $position)) !== false){ 207 | echo $result; 208 | }else{ 209 | echo 'false'; 210 | } 211 | ``` 212 | 213 | ## 4.13 查找通过某个测试的元素 214 | 215 | #### 问题 216 | 217 | 希望找出数组中满足某些需求的元素 218 | 219 | #### 实现 220 | ```php 221 | <?php 222 | $array = range(0, 20,2); 223 | 224 | $result = array_filter($array,function($value){ 225 | if($value%2 === 0){ 226 | return true; 227 | }else { 228 | return false; 229 | } 230 | }); 231 | ?> 232 | ``` 233 | 234 | ## 4.14 查找数组中最大值或最小值元素 235 | 236 | #### 问题 237 | 238 | 希望找出数组中有最大值或最小值的元素。 239 | 240 | 241 | #### 实现 242 | ```php 243 | <?php 244 | $array = range(1, 100); 245 | 246 | echo max($array) . "\n"; 247 | echo min($array) . "\n"; 248 | ``` 249 | 250 | ## 4.15 反转数组 251 | 252 | #### 问题 253 | 254 | 希望反转数组中元素的顺序 255 | 256 | #### 实现 257 | ```php 258 | <?php 259 | 260 | $array = range(1, 100); 261 | $array = array_reverse($array); 262 | print_r($array); 263 | 264 | $fruits = ['a' => 'Apple', 'b' => 'Banana', 'o' => 'orange']; 265 | $fruits = array_reverse($fruits); 266 | print_r($fruits); 267 | ``` 268 | 269 | ## 4.18 多个数据的排序 270 | 271 | #### 问题 272 | 273 | 希望对多个数组或多位数组排序 274 | 275 | #### 实现 276 | ```php 277 | <?php 278 | 279 | $colors = ['Red', 'White', 'Blue']; 280 | $cities = ['Boston', 'New York', 'Chicago']; 281 | $stuff = [ 282 | 'colors' => $colors, 283 | 'cities' => $cities, 284 | ]; 285 | 286 | array_multisort($colors, $cities); 287 | print_r($colors); 288 | print_r($cities); 289 | 290 | 291 | array_multisort($stuff['colors'], $stuff['cities']); 292 | print_r($stuff); 293 | 294 | 295 | $numbers = [0, 1, 2, 3]; 296 | $letters = ['a', 'b', 'c', 'd']; 297 | 298 | array_multisort($numbers, SORT_NUMERIC, SORT_DESC, $letters, SORT_STRING, SORT_DESC); 299 | print_r($numbers); 300 | print_r($letters); 301 | ``` 302 | 303 | ## 4.20 随机调整数组 304 | 305 | #### 问题 306 | 307 | 希望按一种随机的顺序重排数组中的元素 308 | 309 | #### 实现 310 | ```php 311 | <?php 312 | 313 | $name = ['Ann', 'Eason', 'Luis', 'Edware']; 314 | print_r($name); 315 | 316 | shuffle($name); 317 | print_r($name); 318 | ``` 319 | 320 | ## 4.21 删除数组中重复的元素 321 | 322 | #### 问题 323 | 324 | 希望删除数组中重复的元素 325 | 326 | #### 实现 327 | ```php 328 | <?php 329 | 330 | $array = ['Ann', 'Eason', 'Luis', 'Edware', 'Ann', 'Eason', 'Luis', 'Edware']; 331 | print_r($array); 332 | 333 | $unique = array_unique($array); 334 | print_r($unique); 335 | 336 | $unique2 = []; 337 | foreach ($array as $item) { 338 | if (!in_array($item, $unique2)) { 339 | $unique2[] = $item; 340 | } 341 | } 342 | print_r($unique2); 343 | ``` 344 | ## 4.22 对数组中的各个元素应用一个函数 345 | 346 | #### 问题 347 | 348 | 希望对数组中的各个元素应用一个函数或方法,这就允许一次转换所有输入数据 349 | 350 | #### 实现 351 | ```php 352 | <?php 353 | 354 | $names = [ 355 | 'firstName' => 'Ann‘Eason', 356 | 'lastName' => 'Luis’Edware', 357 | ]; 358 | 359 | 360 | array_walk($names, function(&$value, $key) { 361 | $value = htmlentities($value, ENT_QUOTES); 362 | }); 363 | 364 | foreach ($names as $name) { 365 | echo $name . "\n"; 366 | } 367 | 368 | echo '****************************************' . "\n"; 369 | 370 | // 对于嵌套数据,可以使用 array_walk_recursive() 371 | $names = [ 372 | 'firstName' => ['Ann"', 'Luis"'], 373 | 'lastName' => ['"Eason', '"Edware'], 374 | ]; 375 | 376 | array_walk_recursive($names, function(&$value, $key) { 377 | $value = htmlentities($value, ENT_QUOTES); 378 | }); 379 | 380 | foreach ($names as $name) { 381 | foreach ($name as $item) { 382 | echo $item . "\n"; 383 | } 384 | } 385 | ``` 386 | 387 | ## 4.23 查找两个数组的并集、交集或差集 388 | 389 | #### 问题 390 | 391 | 有两个数组,希望找出它们的并集、交集、差集和对称差集 392 | 393 | 394 | #### 实现 395 | ```php 396 | <?php 397 | 398 | $a = range('a', 'n'); 399 | $b = range('h', 't'); 400 | // 要计算并集 401 | $union = array_unique(array_merge($a, $b)); 402 | print_r($union); 403 | 404 | // 要计算交集 405 | $intersection = array_intersect($a, $b); 406 | print_r($intersection); 407 | 408 | // 简单差集 409 | $difference1 = array_diff($a, $b); 410 | $difference2 = array_diff($b, $a); 411 | print_r($difference1); 412 | print_r($difference2); 413 | 414 | // 对称差集 415 | $difference = array_merge(array_diff($a, $b), array_diff($b, $a)); 416 | print_r($difference); 417 | ``` 418 | 419 | ## 4.24 高效迭代处理大型数据集 420 | 421 | #### 问题 422 | 423 | 希望迭代处理一个元素列表,不过整个列表会占用大量内存,或者生成整个列表的速度非常慢。 424 | 425 | #### 实现 426 | 427 | 428 | ## 4.25 使用数组语法访问对象 429 | 430 | #### 问题 431 | 432 | 有一个对象,不过希望它作为一个数组来读写数据。这样不仅可以得到面向对象设计的好处,还可以利用我们熟悉的数组接口 433 | 434 | #### 实现 435 | ```php 436 | <?php 437 | 438 | class FakeArray implements ArrayAccess 439 | { 440 | private $elements; 441 | 442 | public function __construct() 443 | { 444 | $this->elements = []; 445 | } 446 | 447 | public function offsetExists($offset) 448 | { 449 | return isset($this->elements[$offset]); 450 | } 451 | 452 | public function offsetGet($offset) 453 | { 454 | return $this->elements[$offset]; 455 | } 456 | 457 | public function offsetSet($offset, $value) 458 | { 459 | return $this->elements[$offset] = $value; 460 | } 461 | 462 | public function offsetUnset($offset) 463 | { 464 | unset($this->elements[$offset]); 465 | } 466 | } 467 | 468 | $array = new FakeArray; 469 | $array['animal'] = "wabbit"; 470 | 471 | if (isset($array['animal']) && $array['animal'] == 'wabbit') { 472 | unset($array['animal']); 473 | } 474 | 475 | if (!isset($array['animal'])) { 476 | print "Well,What did you expect in an"; 477 | } 478 | ``` 479 | 480 | -------------------------------------------------------------------------------- /博客文章/Swagger-PHP-基础使用.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Swagger-PHP 基础使用 3 | date: 2017-03-02 17:47:47 4 | tags: Swagger 5 | category: PHP 6 | --- 7 | 8 | ## Swagger-PHP 的基本语法 9 | 10 | #### 参考文档 11 | 12 | - [Swagger-Annotations](http://zircote.com/swagger-php/1.x/index.html) 13 | - [Swagger-Explained](http://bfanger.nl/swagger-explained/) 14 | 15 | #### 常用属性 16 | 17 | 红色为必写的字段,蓝色为封装的字段。 18 | 19 | - <font color="blue">@SWG\Swagger</font> 20 | - swagger 21 | - <font color=red>info</font>: <font color="blue">@SWG\Info</font> 22 | - host - 接口域名 23 | - basePath - 接口前缀路径 24 | - schemes: ["http", "https", "ws", "wss"] 25 | - consumes - 请求 Mime Types 26 | - produces - 响应 Mime Types 27 | - paths: <font color="blue">@SWG\Path</font> 28 | - <font color="blue">@SWG\Info</font> 29 | - title - 文档标题 30 | - description - 文档描述 31 | - termsOfService - 所属团队 32 | - contact: <font color="blue">@SWG\Contact</font> - 联系方式 33 | - <font color="blue">@SWG\Contact</font> 34 | - url - 联系链接 35 | - name - 联系名称 36 | - email - 联系邮箱 37 | - <font color="blue">@SWG\License</font> 38 | - <font color=red>name</font> - 开源许可证 39 | - url - 许可证地址 40 | - <font color="blue">@SWG\Path</font> 41 | - get: <font color="blue">@SWG\Get</font> - HTTP Get 请求 42 | - put: <font color="blue">@SWG\Put</font> - HTTP Put 请求 43 | - post: <font color="blue">@SWG\Post</font> - HTTP Post 请求 44 | - delete: <font color="blue">@SWG\Delete</font> - HTTP Delete 请求 45 | - options: <font color="blue">@SWG\Options</font> - HTTP Options 请求 46 | - <font color="blue">@SWG\GET</font> 47 | - tags - 请求分类 48 | - summary - 请求简介 49 | - description - 请求描述 50 | - operationId - 请求编号,要求唯一 51 | - consumes - 请求 Mime Types 52 | - produces - 响应 Mime Types 53 | - parameters: <font color="blue">@SWG\Parameter</font> - 请求参数 54 | - <font color=red>responses</font>: <font color="blue">@SWG\Response</font> - 请求响应 55 | - schemes: ["http","https","ws","wss"] - 请求协议 56 | - deprecated - 是否弃用 57 | - <font color="blue">@SWG\Parameter</font> 58 | - <font color=red>name</font> - 请求参数名称 59 | - <font color=red>in</font>: ["query","header","path","formData","body"] - 请求参数存放方式 60 | - description - 请求参数描述 61 | - required - 是否要求 62 | - <font color=red>schema</font> - 当 `in` 为 `body` 时可以使用,用于描述参数 63 | - <font color=red>type</font>: ["string", "number", "integer", "boolean", "array", "file"] - 请求参数类型 64 | - format: ["int32", "int64", "float", "double", "byte", "date", "date-time"] - 请求参数格式 65 | - allowEmptyValue - 是否允许空值 66 | - items - 当 `type` 为 `array`,`items` 为 `required`,描述参数数组 67 | - collectionFormat 68 | - default - 请求参数默认值 69 | - <font color="blue">@SWG\Response</font> 70 | - <font color=red>default</font> 71 | - response object 72 | - <font color=red>description</font> - 响应描述 73 | - schema: <font color="blue">@SWG\Schema</font> 74 | - headers: <font color="blue">@SWG\Header</font> - 响应头部 75 | - example: <font color="blue">@SWG\Example</font> - 响应数据例子 76 | - reference object 77 | - <font color=red>$ref</font> 78 | - HTTP Status Code 79 | - response object 80 | - <font color=red>description</font> 81 | - schema: <font color="blue">@SWG\Schema</font> 82 | - headers: <font color="blue">@SWG\Header</font> 83 | - example: <font color="blue">@SWG\Example</font> 84 | - reference object 85 | - <font color=red>$ref</font> 86 | - <font color="blue">@SWG\Definition</font> 87 | - definition 88 | - required 89 | - <font color="blue">@SWG\Property</font> 90 | - <font color="blue">@SWG\Property</font> 91 | - property - 模型成员属性 92 | - type: ["string", "number", "integer", "boolean", "array", "file"] - 模型参数类型 93 | - format: ["int32", "int64", "float", "double", "byte", "date", "date-time"] - 模型参数格式 94 | - <font color="blue">@SWG\Header</font> 95 | - header - 头部名称 96 | - type - 头部数值类型 97 | - description - 头部简介 98 | 99 | 100 | ## 模块文件上使用 101 | 102 | 打开文件 `api/modules/v1/module.php`,添加代码如下: 103 | ``` 104 | /** 105 | * v1 module definition class 106 | * 107 | * @SWG\Swagger( 108 | * swagger="2.0", 109 | * @SWG\Info( 110 | * title="安乐窝商品库 API(标题)", 111 | * description="安乐窝商品库 API(描述)", 112 | * termsOfService="安乐窝开发团队", 113 | * version="1.0.0(版本号)", 114 | * @SWG\Contact( 115 | * email="2794408425@qq.com(邮件)", 116 | * name="安乐窝(联系称呼)", 117 | * url="http://api.app/v1/swagger-ui/dist/index.html" 118 | * ), 119 | * @SWG\License( 120 | * name="MIT", 121 | * url="http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT" 122 | * ), 123 | * ), 124 | * host="api.app/", 125 | * basePath="v1/", 126 | * schemes={"http"}, 127 | * produces={"application/json"}, 128 | * consumes={"application/json"}, 129 | * @SWG\Definition( 130 | * definition="ErrorModel", 131 | * required={"code", "message"}, 132 | * @SWG\Property( 133 | * property="code", 134 | * type="integer", 135 | * format="int32" 136 | * ), 137 | * @SWG\Property( 138 | * property="message", 139 | * type="string" 140 | * ) 141 | * ), 142 | * ) 143 | */ 144 | ``` 145 | 146 | 上述代码生成文档时的效果如下图: 147 | 148 | ![](http://o93kt6djh.bkt.clouddn.com/swagger-php-module.png) 149 | 150 | ## 控制器文件上使用 151 | 152 | 打开文件 `api/modules/v1/controllers/MenuController.php` 153 | ``` 154 | /** 155 | * @SWG\Get( 156 | * path="/menu", 157 | * tags={"菜单接口"}, 158 | * description="获取菜单列表", 159 | * operationId="menu/index", 160 | * @SWG\Parameter( 161 | * name="access-token", 162 | * in="query", 163 | * description="Access Token", 164 | * required=true, 165 | * type="string", 166 | * default="HelloWorld" 167 | * ), 168 | * @SWG\Response( 169 | * response=200, 170 | * description="Success Response", 171 | * @SWG\Header(header="x-pagination-per-page", type="string", description="Per Page"), 172 | * @SWG\Header(header="x-pagination-page-count", type="string", description="Page Count"), 173 | * @SWG\Header(header="x-pagination-total-count", type="string", description="Total Count"), 174 | * @SWG\Header(header="x-pagination-current-page", type="string", description="Current Page"), 175 | * @SWG\Schema( 176 | * @SWG\Property( 177 | * property="items", 178 | * type="array", 179 | * @SWG\Items( 180 | * type="object", 181 | * @SWG\Property(property="id", type="integer", example=1), 182 | * @SWG\Property(property="name", type="string", example="权限管理"), 183 | * @SWG\Property(property="route", type="string", example="/admin/permission/index") 184 | * ) 185 | * ), 186 | * @SWG\Property( 187 | * property="_links", 188 | * type="object", 189 | * @SWG\Property( 190 | * property="self", 191 | * type="object", 192 | * @SWG\Property( 193 | * property="href", type="string", example="http://api.app/v1/menu?access-token=HelloWorld&page=1" 194 | * ) 195 | * ), 196 | * ), 197 | * @SWG\Property( 198 | * property="_meta", 199 | * type="object", 200 | * @SWG\Property(property="totalCount", type="integer", example=20), 201 | * @SWG\Property(property="pageCount", type="integer", example=1), 202 | * @SWG\Property(property="currentPage", type="integer", example=1), 203 | * @SWG\Property(property="perPage", type="integer", example=20), 204 | * ) 205 | * ) 206 | * ), 207 | * @SWG\Response( 208 | * response="404", 209 | * description="HTTP 404 Error", 210 | * @SWG\Schema(ref="#/definitions/ErrorModel") 211 | * ), 212 | * @SWG\Response( 213 | * response="500", 214 | * description="HTTP 500 Error", 215 | * @SWG\Schema(ref="#/definitions/ErrorModel") 216 | * ) 217 | * ) 218 | */ 219 | 220 | /** 221 | * @SWG\Post( 222 | * path="/menu", 223 | * tags={"菜单接口"}, 224 | * operationId="menu/create", 225 | * description="新增菜单", 226 | * @SWG\Parameter( 227 | * name="body", 228 | * in="body", 229 | * description="新增菜单所需的参数", 230 | * required=true, 231 | * @SWG\Schema(ref="#/definitions/MenuModel") 232 | * ), 233 | * @SWG\Parameter( 234 | * in="query", 235 | * name="access-token", 236 | * default="HelloWorld", 237 | * description="Access-Token", 238 | * required=true, 239 | * type="string", 240 | * format="string" 241 | * ), 242 | * @SWG\Response( 243 | * response=201, 244 | * description="pet response", 245 | * @SWG\Schema( 246 | * type="object", 247 | * @SWG\Property(property="id", type="integer", example=1), 248 | * @SWG\Property(property="name", type="string", example="歪理兔"), 249 | * @SWG\Property(property="route", type="string", example="/admin/very/too"), 250 | * ) 251 | * ), 252 | * @SWG\Response( 253 | * response="default", 254 | * description="unexpected error", 255 | * @SWG\Schema(ref="#/definitions/ErrorModel") 256 | * ) 257 | * ) 258 | */ 259 | ``` 260 | 261 | 上述代码生成文档时的效果如下图: 262 | 263 | ![](http://o93kt6djh.bkt.clouddn.com/swagger-php-controller1.png) 264 | 265 | ## 模型文件上使用 266 | 267 | ``` 268 | /** 269 | * @SWG\Definition( 270 | * definition="MenuModel", 271 | * required={"id", "name", "route"}, 272 | * @SWG\Property( 273 | * property="id", 274 | * type="integer", 275 | * format="int32" 276 | * ), 277 | * @SWG\Property( 278 | * property="name", 279 | * type="string" 280 | * ), 281 | * @SWG\Property( 282 | * property="route", 283 | * type="string" 284 | * ), 285 | * @SWG\Property( 286 | * property="parent", 287 | * type="integer" 288 | * ), 289 | * @SWG\Property( 290 | * property="order", 291 | * type="integer" 292 | * ), 293 | * @SWG\Property( 294 | * property="data", 295 | * type="string" 296 | * ) 297 | * ) 298 | */ 299 | ``` 300 | -------------------------------------------------------------------------------- /博客文章/Yii-框架之安全篇.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Yii 框架之安全篇 3 | date: 2016-10-29 21:05:35 4 | tags: Yii 5 | category: PHP 6 | --- 7 | 8 | 9 | ## XSS 攻击和防范之存储 10 | 11 | XSS 全称 12 | 13 | XSS 概念 14 | 15 | XSS 是一种经常出现在 Web 应用中的计算机安全漏洞,它允许恶意 Web 用户将代码植入到 16 | 提供给其它用户使用的页面中,从而对用户进行攻击 17 | 18 | ### 如何使用 XSS 攻击盗取用户账号 19 | ``` 20 | var cookie = document.cookie; 21 | window.location.href = "http://127.0.0.1/index.php?cookie="+cookie; 22 | ``` 23 | 24 | - 问题如下:document.cookie 获取得到的 cookie 是什么样的 25 | - index.php 是如何处理 cookie 值的 26 | - 有了 cookie 怎么就能进行盗取账号的 27 | 28 | httponly 阻止 js 读取 cookie 29 | 30 | 哪一段 js 代码能够自动转账 31 | ``` 32 | document.getElementById('ipt-search-key').value='withyz@xx.com'; 33 | document.getElementById('amount').value='100'; 34 | document.getElementById('reason').value='劫富济贫'; 35 | document.getElementsByClassName('ui-button-text')[0].click(); 36 | ``` 37 | 如何把 js 注入到转账页面中 38 | 39 | 40 | ## XSS 攻击和防范之反射 41 | 42 | 现代现浏览器(chrome)对于反射型XSS有一定防护 43 | 手动关闭:\Yii::$app->response->headers->add('X-XSS-Protection', '0'); 44 | 45 | HTML 实体编码 46 | URL 编码 47 | 48 | ## XSS 攻击和防范之蠕虫 49 | 50 | ### Yii 如何防范 XSS 51 | 52 | 转码防范 53 | \yii\helpers\Html::encode($script); 54 | 55 | 过滤防范 56 | \yii\helpers\HtmlPurifier::process($js); 57 | 58 | ## CSRF 攻击和防范 59 | 60 | 跨站请求伪造 61 | 62 | get型的crtf攻击 63 | 构造url, 诱使用户点击 64 | 65 | 透过例子能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。 66 | 67 | yii 框架 csrf token 验证过程 68 | 69 | 检查Referer字段 70 | HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。 71 | 这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。 72 | 添加校验token 73 | 由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。 74 | 75 | 76 | ## SQL 注入和防范 77 | 78 | addslashes() 转义函数 防范 SQL 注入 79 | 80 | Sql注入:将要执行的sql语句采用拼接的方式组装时,就sql注入的可能; 81 | 原本要查询的字符串在拼接后发生发“越狱”,部分字符串被数据库识别成可执行语句, 82 | 导致意外的操作和查询结果 83 | 防范:1,屏蔽关键字和敏感词,有影响业务逻辑的可能; 84 | 2, 对传入变量转义,避免变量的内容越狱 85 | 86 | 绕过转义: 87 | char(0xdf)/',在utf-8下会变成β/', 88 | 而在gbk下由于汉字是2个字节组成;在数据中将变成 運',单引号逃过了被转义 89 | 90 | 使用PDO统一数据库接口,可以无痛切换; 91 | 使用占位符防范SQL注入 92 | 93 | 94 | ## 文件上传漏洞 95 | 96 | 文件上传漏洞 97 | 如果后台对文件上传审查不严,导致php代码上传后被执行,可进行遍历文件等操作使源码泄露 98 | 99 | 用 fiddler 可以拦截请求,然后修改请求的内容,比如图片中的文件名,修改成如上形式后,会生成一个php文件,然后在上传后的目录执行php文件会造成危害 100 | 101 | 保存上传过来的文件名的时候不要用用户本来的名称,容易在中途被非法修改,从而变成.php这些可执行文件,造成损害 102 | -------------------------------------------------------------------------------- /博客文章/Yii-框架之工具篇.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Yii 框架之工具篇 3 | date: 2016-11-03 20:45:23 4 | tags: Yii 5 | category: PHP 6 | --- 7 | 8 | ## Composer 工具 9 | 10 | 11 | ## Debug 工具 12 | 13 | 14 | ## GII 工具 15 | 16 | -------------------------------------------------------------------------------- /博客文章/Yii-框架之扩展篇.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Yii 框架之扩展篇 3 | date: 2016-10-29 16:13:23 4 | tags: Yii 5 | category: PHP 6 | --- 7 | 8 | 9 | ## Yii 扩展分类 10 | 11 | - 模块化 12 | - 事件机制 13 | - MixIN (混合、多重继承) 14 | - 依赖注入 15 | 16 | 17 | ## 模块化技术 18 | 19 | 模块化:通过对业务详细拆分,分化出不同的小模块, 20 | 可以通过思维导图进行梳理 21 | 系统加载模块时通过配置文件进行控制,若模块暂不可用可于配置中标明以通知系统模块暂不可用 22 | 23 | 使用 Gii 的 Module Generator 24 | 25 | 模块化实现:<br> 26 | 1、父级模块化(1级)<br> 27 | 通过gii的modules生成对应的子模块(2级),<br> 28 | 然后修改config.php中的web.php的配置信息,添加以下信息(例子):<br> 29 | 'modules'=>['article'=>'app/modules/article/Article']<br> 30 | 访问方式,例如:localhost/basic/index.php?r=article/default/index 31 | 2、2级模块化(1级),类推<br> 32 | 通过gii的modules生成对应的子模块(3级),<br> 33 | 然后修改该模块下的.php配置信息,添加以下信息(例子):<br> 34 | 在actionInit中添加如下信息:<br> 35 | $this->modules = ['category'=>['class'=>'app/modules/article/modules/test/Test']]; 36 | 访问方式,例如:localhost/basic/index.php?r=article/test/default/index 37 | 38 | ## 事件机制 39 | 40 | <?php 41 | namespace app\controllers; 42 | use yii\web\Controller; 43 | use vendor\animal\Cat; 44 | use vendor\animal\Mourse; 45 | use vendor\animal\Dog; 46 | use \yii\base\Event; 47 | class AnimalController extends Controller 48 | { 49 | public function actionIndex(){ 50 | $cat = new Cat(); 51 | $cat2 = new Cat(); 52 | $mourse = new Mourse(); 53 | $dog = new Dog(); 54 | /* 55 | //案例一 56 | //实现猫叫,老鼠跑绑定事件,dog关注 57 | $cat->on('miao',[$mourse,'run']); //on()方法来自于Componment,on()方法实现绑定事件 58 | //猫叫的时候给老鼠传递一些参数信息 59 | 60 | $cat->on('miao',[$dog,'look']);//dog关注cat的miao 61 | #-*/ 62 | /* 63 | //案例二 64 | //实现猫叫,老鼠跑绑定事件,dog关注后,取消关注 65 | $cat->on('miao',[$mourse,'run']); //on()方法来自于Componment,on()方法实现绑定事件 66 | //猫叫的时候给老鼠传递一些参数信息 67 | 68 | $cat->on('miao',[$dog,'look']);//dog关注cat的miao 69 | $cat->off('miao',[$dog,'look']);//dog取消关注cat的miao 70 | #---*/ 71 | 72 | 73 | ## MixIn 74 | 75 | Behavior 76 | 77 | ## 依赖注入 78 | 79 | 80 | -------------------------------------------------------------------------------- /博客文章/Yii-框架之高效篇.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Yii 框架之高效篇 3 | date: 2016-10-25 20:42:56 4 | tags: Yii 5 | category: PHP 6 | --- 7 | 8 | ## Yii 的延迟加载 9 | 10 | ### 类的延迟加载 11 | sql_autoload_register 12 | 13 | Yii 如何使用 sql_autoload_register 14 | 15 | 自动加载文件路径:vendor/yiisoft/yii2/Yii.php 16 | 入口文件路径:web/index.php 17 | 18 | ### 类的映射表机制 19 | 20 | yii2快速的机制:使用类的映射表,把常用的类放入映射表中,加快加载速度 21 | 22 | 映射表文件:vendor/yiisoft/yii2/classes.php 23 | 类的映射变量:Yii::$classMap 24 | vendor/yiisoft/yii2/BaseYii.php 25 | 26 | 可以通过使用Yii::$classMap,对延迟加载机制进行优化,是典型的空间换时间的做法,所以不建议往classMap中放入太多不常用的内容,避免内存占用过多 27 | 28 | 只建议将常用的高频率class放进‘类的映射表’,不常用的就不要放,否则映射表大了,不但降低查询效率,还将占用不必要的内存。 29 | 30 | ### 组件的延迟加载 31 | 32 | - index.php 33 | - app 34 | - application class 35 | - components 36 | - vendor/yiisoft/yii2/web/Application.php 37 | - vendor/yiisoft/yii2/base/Application.php 38 | - controller 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 | yii框架缓存的DB依赖,跟其他缓存依赖一样,如果依赖中的sql取出的数据发生了改变,相关联的缓存就会消失 65 | 66 | 67 | 表达式依赖 68 | 69 | 1、文件依赖(FileDependency):一旦文件改变,缓存将失效 70 | $dependency=new \yii\caching\FileDependency(['filename'=>'hw.txt']); 71 | $cache->add('file_key','hello word!',3000,$dependency); 72 | 2、表达式依赖(ExpressionDependency):一旦表达式改变,缓存将失效,即参数发生了变化 73 | $dependency=new \yii\caching\ExpressionDependency(['expression'=>'\YII::$app->request->get["name"]']); 74 | 3、DB依赖(DbDependency):一旦数据改变,缓存将失效 75 | $dependency=new \yii\caching\ExpressionDependency( 76 | ['sql'=>'select count(*) from user']); 77 | 78 | 4.链式依赖 79 | 5.组依赖 80 | 81 | Yii 框架是如何监听文件改变、表达式改变和数据改变? 82 | 83 | ## 片段缓存的使用 84 | 85 | //片段缓存介绍(主要负责把前端界面的一些区域[不会经常变动的区域:如京东商品分类]缓存起来[缓存到内存或文件中],下次访问时直接从缓存里把数据拿出来,而不用再从数据库抓取信息,提高了程序的执行效率)。 86 | 87 | 88 | ``` 89 | <!-- 使用视图组件($this)里的beginCache('缓存数据的名字')方法把cache_div给缓存起来;这个方法开启会检查当前有没有缓存,如果没有返回false --> 90 | <!-- 如何证明cache_div的代码片段被缓存了呢? 在两个div里都添加上相同的内容。如果cache_div被缓存起来将会使用缓存里的内容,而不会使用修改后的内容。提高了程序的运行效率。 --> 91 | <?php if($this->beginCache('cache_div')){?> 92 | <div id='cache_div'> 93 | <div>这里待会会被缓存(这里是缓存过后添加的)</div> 94 | </div> 95 | <?php 96 | $this->endCache();//结束缓存 97 | } 98 | ?> 99 | 100 | <div id='no_cache_div'> 101 | <div>这里不会被缓存(这里是缓存过后添加的)</div> 102 | </div> 103 | ``` 104 | 105 | 片段缓存的设置 106 | 107 | 片段缓存的时间 108 | $this->beginCache('cache_div',['duration'=>$duration]); 109 | $this->endCache(); 110 | 111 | 片段缓存的依赖 112 | $this->beginCache('cache_div',['dependency'=>$dependency]); 113 | $this->endCache(); 114 | 115 | 片段缓存的开关 116 | $this->beginCache('cache_div',['enabled'=>$enabled']); 117 | $this->endCache(); 118 | 119 | 片段缓存的嵌套使用 120 | 121 | 谨慎使用:外层的失效时间应该短于内层,外层的依赖条件应该低于内层,以确保最小的片段,返回的是最新的数据。 122 | 123 | 片段嵌套缓存 外面的缓存时间大于里面的 那么里面的数据即使超过了缓存时间后有修改也不会被执行 124 | 125 | ## 页面缓存的使用 126 | 127 | ``` 128 | public function actionIndex() 129 | { 130 | 131 | } 132 | 133 | //页面访问index()方法之前会先访问behavior()方法行为。 134 | public function behavior() 135 | { 136 | return [ 137 | [ 138 | 'class' => 'yii\filters\PageCache',//页面缓存类 139 | 'duration' => 1000,//缓存时间 140 | 'only' => ['index', 'test'],//仅仅缓存index和test方法的数据] 141 | 'dependency' => ['class' => 'yii\caching\FileDependency', 'fileName' => 'hw.txt']//文件缓存依赖 142 | ], 143 | ]; 144 | } 145 | ``` 146 | 147 | 148 | 149 | ## HTTP 缓存的使用 150 | 151 | 如何校验服务器与浏览器的数据一致 152 | 153 | 用矩形L代表浏览器,矩形F代表服务器。 154 | 1.当用户到F浏览网站或一个页面时,就会用L给F发送一个请求,然后F就会处理这个请求,处理完之后会把处理的结果作为一个响应返回给L,然后L从这个响应里拿数据,再显示出来,这样用户就可以看到他想看到的页面了。 155 | 2.但这时会遇到一个小问题,如果用户从F拿过来的数据不是他想要的数据(比如12306订票页面想订的票没有了),这时用户猛刷新L,那么L就会给F发送很多的请求,实际上F的数据是没有发生变化的,F收到这么多请求后都会照常的处理,但处理的结果没有发生变化,所以处理的结果跟第一次返回出去的数据是一样的。所以这样对F来说,很浪费资源、时间。 156 | 3.F想了一个办法,当F第一次把数据(小圆表示)发送给L时,它让L把数据缓存到L里面,也就是把它缓存起来,缓存起来以后如果下一次L发送了请求到了F这边之后,F会对比F这边的数据和L那边的数据是不是一样的(F是如何校验的呢?),如果是一样的那就没必要再把这个数据发送回给L了,就让L使用缓存里的数据就行了。 157 | 4.F如何校验F和L的数据是一样的,F就想了一个办法。当F第一次发送数据给L时,会在数据中塞入一个last-modified字段(记录了F这边数据的修改时间),那么L也有了这个字段。那么当L下一次发送请求时,会把这个修改时间也带上,也就是把lm(last-modified字段)发送回给F,F会校验lm和F里的那个数据的修改时间是否是一样的,如果是一样的那么这个数据肯定没有被修改过,那就没必要再发送回给L了,直接告诉L去使用缓存里的数据就行了。 158 | 5.另一种校验方式是基于last-modified的小缺陷设计出来的(开发人员把F的数据@改为#,然后又改回@,那么数据@虽然没变化,但它的修改时间改变了。那么L发送过来的lm和数据@的修改时间是对不上的,然后F又把@发送给L,但实际上@没有发生变化,这样也会造成一些问题)。所以F想了一个办法,F第一次发送@数据包时,会根据@里的内容生成一个标志etag,通过etag可以大概了解数据包@里的内容。F会把etag和last-modified一起发送给L,那么L下一次访问F时,会传lm和etag。那么F先验证lm(两边数据修改时间是否一样),一样则使用L的缓存;如果不一样,F则验证etag是否一样,如果一样证明@没有被修改,则让L使用缓存 159 | 160 | 161 | 服务器响应304,not modifired 162 | 163 | 服务器怎么告诉浏览器缓存起来? 164 | 165 | ``` 166 | public function behaviors(){ 167 | return [ 168 | [ 169 | 'class'=>'yii\filters\HttpCache', 170 | 'lastModified'=>function(){ 171 | return 1432817564; 172 | }, 173 | 'etagSeed'=>function(){ 174 | return 'etagseed2f;' 175 | } 176 | ] 177 | ]; 178 | } 179 | ``` 180 | //使用 HttpCache 之后再请求头部header会多了一个Cache-Control:last-modified 来作为标志 181 | 182 | 183 | 184 | 在服务器修改数据之后如何让浏览器更新缓存 185 | 186 | httpCache的依赖: 187 | lastModified: 时间标识 188 | etagSeed:内容标识 189 | 当二者同时存时,需要两个均发生变化才会发送200并返回新数据 190 | 191 | 192 | ## gii 工具简介 193 | 194 | - Model Generator(数据模型的代码生成器) 195 | - Form Generator(表单的代码生成器) 196 | - Module Generator(模块的代码生成器) 197 | - CRUD Generator(增删改查的代码生成器) 198 | - Controller Generator(控制器的代码生成器) 199 | - Extension Generator(扩展的代码生成器) 200 | -------------------------------------------------------------------------------- /博客文章/Yii2-Working-with-Databases.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Yii2 Working with Databases 3 | date: 2016-12-02 11:09:02 4 | tags: Yii 5 | category: PHP 6 | --- 7 | 8 | ## Create 9 | ```php 10 | <?php 11 | // 单条数据插入 12 | Yii::$app->db->createCommand()->insert('shop_user', [ 13 | 'userName' => 'HelloWorld', 14 | 'userPass' => md5(123456), 15 | 'userEmail' => 'hello@world.com', 16 | 'createdAt' => time(), 17 | ])->execute(); 18 | 19 | $customer = new Customer(); 20 | $customer->name = 'Ann'; 21 | $customer->email = 'AnnEason@qq.com'; 22 | $customer->save(); 23 | 24 | // 批量数据插入 25 | Yii::$app->db->createCommand()->batchInsert('shop_user', 26 | ['userName', 'userPass', 'userEmail', 'createdAt'], 27 | [ 28 | ['Ann', md5(123456), 'Ann@qq.com', time()], 29 | ['Leon', md5(123456), 'Leon@qq.com', time()], 30 | ['bitch', md5(123456), 'bitch@qq.com', time()], 31 | ['Linda', md5(123456), 'Linda@qq.com', time()], 32 | ] 33 | )->execute(); 34 | ``` 35 | 36 | ## Update 37 | ```php 38 | <?php 39 | // 数据更新方法(表名, 字段与值, 条件) 40 | Yii::$app->db->createCommand()->update('shop_user', ['userName' => 'hello'], 'userId = 6')->execute(); 41 | 42 | $customer = Customer::findOne(123); 43 | $customer->email = 'AnnEason@qq.com'; 44 | $customer->save(); 45 | 46 | $values = [ 47 | 'name' => 'Ann', 48 | 'email' => 'Ann@qq.com' 49 | ]; 50 | 51 | $customer = new Customer(); 52 | $customer->attributes = $values; 53 | $customer->save(); 54 | 55 | Customer::updateAll(['status' => Customer::STATUS_ACTIVE],['like','email','@example.com']); 56 | ``` 57 | 58 | ## Retrieve 59 | ```php 60 | <?php 61 | // 存在数据返回二维数组,没有数据返回空数组 62 | $orders = Yii::$app->db->createCommand("select * from z_order order by id desc limit 100 ;")->queryAll(); 63 | $rows = (new \yii\db\Query())->select(['id','email'])->from('user') ->all(); 64 | $models = Model::find()->where(['status' => Model::STATUS])->all(); 65 | 66 | // 存在数据返回一维数组,没有数据返回 false 67 | $order = Yii::$app->db->createCommand("select * from z_order where id = 802478;")->queryOne(); 68 | $row = (new \yii\db\Query())->from('user')->where(['like','username','test'])->one(); 69 | $model = Model::find()->where(['id'=>1])->one(); 70 | 71 | // 存在数据返回一维数组,没有数据返回空数组 72 | $orderNumbers = Yii::$app->db->createCommand("select orderno from z_order order by id desc limit 100") 73 | ->queryColumn(); 74 | $columns = (new \yii\db\Query())->select('id')->from('user')->column() 75 | 76 | // 存在数据返回标量数值,没有数据返回 false 77 | $orderCount = Yii::$app->db->createCommand("select count(orderno) from z_order")->queryScalar(); 78 | $count = (new \yii\db\Query())->from('user')->where(['like','username','test'])->count(); 79 | $modelCount = Model::find()->where(['status'=>1])->count(); 80 | ``` 81 | 82 | ## Delete 83 | ```php 84 | <?php 85 | // 数据删除方法(表名,条件) 86 | Yii::$app->db->createCommand()->delete('shop_user', 'userName = "HelloWorld"')->execute(); 87 | 88 | $customer = Customer::findOne(123); 89 | $customer->delete(); 90 | 91 | Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]); 92 | ``` 93 | 94 | ## Where 95 | ```php 96 | <?php 97 | // String Format 98 | $query->where('status = 1'); 99 | $query->where('status = :status', [':status' => $status]); 100 | $query->where('status = :status')->addParams([':status' => $status]); 101 | 102 | // Hash Format ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15)) 103 | $query->where([ 104 | 'status' => 10, 105 | 'type' => null, 106 | 'id' => [4, 8, 15], 107 | ]); 108 | 109 | // Operator Format [operator, operand1, operand2, ...] 110 | $query->where(['and', 'id=1', 'id=2']); 111 | $query->where(['and', 'type=1', ['or', 'id=1', 'id=2']]); 112 | 113 | $query->where(['between', 'id', 1, 10]); 114 | $query->where(['not between', 'id', 1, 10]); 115 | 116 | $query->where(['in', 'id', [1, 2, 3, 4, 5]]); 117 | $query->where(['not in', 'id', [1, 2, 3, 4, 5]]); 118 | 119 | $query->where(['like', 'name', 'hello']); 120 | $query->where(['like', 'name', ['hello', 'world']]); 121 | 122 | $query->where(['>', 'age', 10]) 123 | $query->andWhere(['like', 'title', 'hello-world']); 124 | $query->orWhere(['like','name','LuisEdware']); 125 | 126 | $query->filterWhere(['name' => $name, 'email' => $email]); 127 | $query->andFilterWhere(['!=', 'id', 1]); 128 | $query->orFilterWhere(['status' => 2]); 129 | 130 | $query->andFilterCompare('name', 'John Doe'); 131 | ``` 132 | 133 | ## orderBy 134 | ```php 135 | <?php 136 | $query->orderBy([ 137 | 'id' => SORT_ASC, 138 | 'name' => SORT_DESC, 139 | ]); 140 | 141 | $query->orderBy('id ASC, name DESC'); 142 | 143 | $query->orderBy('id ASC')->addOrderBy('name DESC'); 144 | ``` 145 | 146 | ## groupBy 147 | ```php 148 | <?php 149 | 150 | $query->groupBy(['id','status']); 151 | 152 | $query->groupBy('id,status'); 153 | 154 | $query->groupBy(['id','status'])->addGroupBy('age'); 155 | ``` 156 | 157 | ## having 158 | ```php 159 | <?php 160 | 161 | $query->having(['status'=>1]); 162 | 163 | $query->having(['status'=>1])->andHaving(['>','age',30]); 164 | ``` 165 | 166 | ## limit and offset 167 | ```php 168 | <?php 169 | $query->limit(10)->offset(20); 170 | ``` 171 | 172 | ## join 173 | ```php 174 | <?php 175 | // ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id` 176 | $query->join('LEFT JOIN','post','post.user_id = user.id'); 177 | ``` 178 | 179 | ## union 180 | ```php 181 | <?php 182 | $query1 = (new \yii\db\Query()) 183 | ->select('id,category_id AS type, name') 184 | ->from('post') 185 | ->limit(10); 186 | 187 | $query2 = (new \yii\db\Query()) 188 | ->select('id,type,name') 189 | ->from('user') 190 | ->limit(10); 191 | 192 | $query1->union($query2); 193 | ``` 194 | 195 | ## find 196 | ```php 197 | <?php 198 | // SELECT * FROM customer WHERE id = 123; 199 | $customer = Customer::findOne(123); 200 | 201 | // SELECT * FROM customer WHERE id IN (1,2,3,4,5) 202 | $customers = Customer::findOne([1,2,3,4,5]); 203 | 204 | $customer = Customer::findOne(['id'=>123, 'status'=> Customer::STATUS]); 205 | $customers = Customer::findAll(['id'=>123, 'status'=> Customer::STATUS_ACTIVE]); 206 | ``` 207 | 208 | ## Relations 209 | ```php 210 | <?php 211 | class Order extends ActiveRecord 212 | { 213 | public function getOrderItems() 214 | { 215 | return $this->hasMany(OrderItem::className(), ['order_id' => 'id']); 216 | } 217 | 218 | public function getItems() 219 | { 220 | return $this->hasMany(Item::className(), ['id' => 'item_id']) 221 | ->via('orderItems'); 222 | } 223 | } 224 | $items = $order->items; 225 | 226 | 227 | $customers = Customer::find()->with('orders.items')->all(); 228 | // no SQL executed 229 | $items = $customers[0]->orders[0]->items; 230 | ``` 231 | 232 | 233 | -------------------------------------------------------------------------------- /博客文章/「Tips」-htaccess-配置-HTTP-Header.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 「Tips」.htaccess 配置 HTTP Header 3 | date: 2017-03-15 17:18:21 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | 使用 Yii 2 做 RESTFul API 项目时,发现在前端发起一个请求时,会出现以下错误: 9 | ```console 10 | Request header field access-token is not allowed by Access-Control-Allow-Headers in preflight response. 11 | ``` 12 | 13 | 排查问题进行了很久,最后才发现在 Yii 2 的 `web` 目录下,有个文件 `.htaccess`,是可以配置 `Apache` 服务器接受 HTTP 请求的头部信息的,在特定选项加入 `access-token` 即刻不再报错,因为本次项目使用 `HTTP Header` 传递 `access-token` 的,没有加入 `access-token` 就意味 `Apache` 服务器不接受头部带有 `access-token` 的 HTTP 请求 。文件配置代码如下: 14 | ```apache 15 | Header always set Access-Control-Allow-Origin "*" 16 | Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT" 17 | Header always set Access-Control-Max-Age "1000" 18 | Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token, access-token" 19 | 20 | RewriteEngine on 21 | 22 | RewriteCond %{REQUEST_METHOD} OPTIONS 23 | RewriteRule ^(.*)$ $1 [R=200,L] 24 | 25 | RewriteCond %{REQUEST_FILENAME} !-f 26 | RewriteCond %{REQUEST_FILENAME} !-d 27 | RewriteRule . index.php 28 | ``` 29 | -------------------------------------------------------------------------------- /博客文章/「Tips」PHP-7-1-命名空间的新特性和使用方法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 「Tips」PHP 7.1 命名空间的新特性和使用方法 3 | date: 2017-03-15 17:16:02 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | 新特性和使用方法如下: 9 | ```php 10 | <?php 11 | 12 | // Pre PHP 7 code 13 | use some\namespace\ClassA; 14 | use some\namespace\ClassB; 15 | use some\namespace\ClassC as C; 16 | 17 | use function some\namespace\fn_a; 18 | use function some\namespace\fn_b; 19 | use function some\namespace\fn_c; 20 | 21 | use const some\namespace\ConstA; 22 | use const some\namespace\ConstB; 23 | use const some\namespace\ConstC; 24 | 25 | // PHP 7+ code 26 | use some\namespace\{ClassA, ClassB, ClassC as C}; 27 | use function some\namespace\{fn_a, fn_b, fn_c}; 28 | use const some\namespace\{ConstA, ConstB, ConstC}; 29 | ``` 30 | -------------------------------------------------------------------------------- /博客文章/「Tips」Yii2-前端请求的参数字段映射数据模型的属性.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 「Tips」Yii2 前端请求的参数字段映射数据模型的属性 3 | date: 2017-03-15 17:18:34 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | 使用 Yii 2 构建 RESTFul API 项目时,需要实现一个功能。这个功能可以将 HTTP 请求的参数和后端数据模型相映射,让前端的请求参数命名和后端数据的模型属性不需要保持一致,并在指定场景中实现数据验证。实现代码如下: 9 | ```php 10 | <?php 11 | /* 12 | * 前端传递 JSON 数据进来,格式是{"name":"admin","pwd":"123456"} 13 | * 需要对数据在指定场景下验证,并对应到模型的 username 属性和 password_hash 属性 14 | */ 15 | 16 | // 模型代码 17 | class SignInForm 18 | { 19 | public $name; 20 | public $pwd; 21 | 22 | public function rules() 23 | { 24 | $rules = [ 25 | [ 26 | [ 27 | 'name', 28 | 'pwd', 29 | ], 30 | 'required', 31 | 'on' => 'signIn', 32 | ], 33 | [ 34 | ['name'], 35 | 'default', 36 | 'value' => $this->username, 37 | 'on' => 'signIn', 38 | ], 39 | [ 40 | ['pwd'], 41 | 'default', 42 | 'value' => $this->password_hash, 43 | 'on' => 'signIn', 44 | ], 45 | ]; 46 | 47 | return $rules; 48 | } 49 | } 50 | 51 | // 控制器代码 52 | <?php 53 | 54 | use Yii; 55 | use workerbee\api\controllers\BaseController; 56 | 57 | class UserController extends BaseController 58 | { 59 | public function actionSignIn() 60 | { 61 | $data['SignInForm'] = Yii::$app->request->post(); 62 | $signInModel = new SignInForm(['scenario' => 'signIn']); 63 | 64 | if ($signInModel->load($data) && $signInModel->validate()) { 65 | $user = SignInForm::findByUsername($signInModel->username); 66 | $validator = Yii::$app->getSecurity()->validatePassword($signInModel->pwd, $user->password_hash); 67 | if ($user !== null && $validator) { 68 | $user->access_token = $signInModel->getNewAccessToken(); 69 | if ($user->save()) { 70 | return $user; 71 | } 72 | 73 | throw new ServerErrorHttpException('登陆失败.'); 74 | } 75 | 76 | throw new BadRequestHttpException('账号名或密码错误.'); 77 | } 78 | 79 | throw new UnprocessableEntityHttpException('数据验证失败.'); 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /博客文章/「复习」使用阿里云从零开始部署-Laravel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 「复习」使用阿里云从零开始部署 Laravel 3 | date: 2017-02-23 14:35:50 4 | tags: AliCloud 5 | category: Tooling 6 | --- 7 | 8 | ## 准备 9 | 10 | 更新 Yum 11 | ``` 12 | yum update 13 | ``` 14 | 15 | 更新 Yum 源 16 | ``` 17 | rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm 18 | rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm 19 | rpm -Uvh http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 20 | ``` 21 | 22 | 安装 Git 23 | ``` 24 | yum install -y git 25 | ``` 26 | 27 | 安装 Htop 28 | ``` 29 | yum install -y htop 30 | ``` 31 | 32 | 安装 Vim 33 | ``` 34 | yum install -y vim 35 | ``` 36 | 37 | ## Nginx 38 | 39 | 安装 Nginx 代码仓库 40 | ``` 41 | yum install -y epel-release 42 | ``` 43 | 44 | 安装 Nginx 45 | ``` 46 | yum install -y nginx 47 | ``` 48 | 49 | 启动 Nginx 50 | ``` 51 | systemctl start nginx 52 | ``` 53 | 54 | 开机启动 Nginx 55 | ``` 56 | systemctl enable nginx 57 | ``` 58 | 59 | Nginx 配置文件 60 | 61 | - /usr/share/nginx/html 默认的代码目录 62 | - /etc/nginx/conf.d/default.conf 默认的配置文件 63 | 64 | ## MariaDB 65 | 66 | 安装 MariaDB 67 | ``` 68 | yum install -y mariadb-server mariadb 69 | ``` 70 | 71 | 启动 MariaDB 72 | ``` 73 | systemctl start mariadb 74 | ``` 75 | 76 | 初始化 MariaDB 77 | ``` 78 | mysql_secure_installation 79 | ``` 80 | 81 | 开机启动 MariaDB 82 | ``` 83 | systemctl enable mariadb 84 | ``` 85 | 86 | ## PHP 87 | 88 | 安装 PHP 89 | ``` 90 | yum install -y php71w php71w-mysql php71w-xml php71w-soap php71w-xmlrpc php71w-mbstring php71w-json php71w-gd php71w-mcrypt php71w-fpm php71w-pdo php71w-pecl-redis 91 | ``` 92 | 93 | 启动 PHP-FPM 94 | ``` 95 | systemctl start php-fpm 96 | ``` 97 | 98 | 开机启动 99 | ``` 100 | systemctl enable php-fpm 101 | ``` 102 | 103 | 配置 PHP 关联 Nginx,编辑 Nginx 配置文件如下: 104 | ```nginx 105 | server{ 106 | listen 80; 107 | server_name cowcat.cc; 108 | 109 | # note that these lines are originally from the "location /" block 110 | root /usr/share/nginx/html; 111 | index index.php index.html index.htm; 112 | 113 | location / { 114 | try_files $uri $uri/ =404; 115 | } 116 | error_page 404 /404.html; 117 | error_page 500 502 503 504 /50x.html; 118 | location = /50x.html { 119 | root /usr/share/nginx/html; 120 | } 121 | 122 | location ~ \.php$ { 123 | try_files $uri =404; 124 | fastcgi_pass 127.0.0.1:9000; 125 | fastcgi_index index.php; 126 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 127 | include fastcgi_params; 128 | } 129 | } 130 | ``` 131 | 132 | 在 `/usr/share/nginx/html` 新增文件 `index.php`,编辑代码如下: 133 | ```php 134 | <?php 135 | phpinfo(); 136 | ``` 137 | 138 | 输入域名,检查 PHP 是否与 Nginx 关联起来 139 | 140 | ## Redis 141 | 142 | 安装 Redis 143 | ``` 144 | wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-9.noarch.rpm 145 | rpm -ivh epel-release-7-9.noarch.rpm 146 | yum install -y redis 147 | ``` 148 | 149 | 启动 Redis 150 | ``` 151 | systemctl start redis 152 | ``` 153 | 154 | 开机启动 Redis 155 | ``` 156 | systemctl enable redis 157 | ``` 158 | ## Beanstalkd 159 | 160 | 安装 Beanstalkd 161 | ``` 162 | wget http://cbs.centos.org/kojifiles/packages/beanstalkd/1.9/3.el7/x86_64/beanstalkd-1.9-3.el7.x86_64.rpm 163 | wget http://cbs.centos.org/kojifiles/packages/beanstalkd/1.9/3.el7/x86_64/beanstalkd-debuginfo-1.9-3.el7.x86_64.rpm 164 | rpm -ivh beanstalkd-1.9-3.el7.x86_64.rpm 165 | rpm -ivh beanstalkd-debuginfo-1.9-3.el7.x86_64.rpm 166 | yum install -y beanstalkd 167 | ``` 168 | 169 | 启动 Beanstalkd 170 | ``` 171 | systemctl start beanstalkd 172 | ``` 173 | 174 | 开机启动 175 | ``` 176 | systemctl enable beanstalkd 177 | ``` 178 | 179 | ## Composer 180 | 181 | 安装 Composer 182 | ``` 183 | curl -sS https://getcomposer.org/installer | php 184 | mv composer.phar /usr/local/bin/composer 185 | composer -v 186 | ``` 187 | 188 | 全局设置国内镜像 189 | ``` 190 | composer config -g repo.packagist composer https://packagist.phpcomposer.com 191 | ``` 192 | 193 | ## Node.js 194 | 195 | 安装 Node.js 196 | ``` 197 | yum install -y gcc-c++ make 198 | yum install -y nodejs 199 | ``` 200 | 201 | 安装 webpack、gulp、bower 202 | ``` 203 | npm install bower -g 204 | npm install gulp -g 205 | npm install webpack -g 206 | ``` 207 | 208 | ## Gogs 209 | 210 | 设置子域名 211 | ``` 212 | hostnamectl set-hostname git.cowcat.cc 213 | ``` 214 | 215 | 禁用 SELinux 相关选项 216 | ``` 217 | sed -i /etc/selinux/config -r -e 's/^SELINUX=.*/SELINUX=disabled/g' 218 | systemctl reboot 219 | ``` 220 | 221 | 创建 gogs 数据库 222 | ``` 223 | CREATE DATABASE IF NOT EXISTS gogs CHARACTER SET utf8 COLLATE utf8_general_ci; 224 | use gogs; 225 | SET default_storage_engine=INNODB; 226 | ``` 227 | 228 | 安装 Go 229 | ``` 230 | yum install -y go 231 | ``` 232 | 233 | 安装 Gogs 234 | ``` 235 | // 下载二进制包 236 | wget http://7d9nal.com2.z0.glb.qiniucdn.com/0.10rc/linux_amd64.zip 237 | 238 | // 解压 239 | unzip linux_amd64.zip 240 | 241 | cd gogs/ 242 | // 后台运行 243 | nohup ./gogs web & 244 | tail -f nohup.out 245 | ``` 246 | 247 | 访问 URL 进行安装,安装 URL 是主机 IP 地址加上 3000 端口。 248 | 249 | 以守护进程的形式运行 Gogs:[https://github.com/gogits/gogs/blob/master/scripts/init/centos/gogs](https://github.com/gogits/gogs/blob/master/scripts/init/centos/gogs) 250 | 251 | 252 | ## Walle 253 | 254 | 进入 MySQL,新增数据库如下: 255 | ``` 256 | create database walle default character set utf8 COLLATE utf8_general_ci; 257 | ``` 258 | 259 | 安装依赖 260 | ``` 261 | yum install ansible 262 | ``` 263 | 264 | 代码检出 265 | ``` 266 | mkdir -p /data/www/ # 新建目录 267 | git clone https://github.com/meolu/walle-web.git # 代码检出 268 | ``` 269 | 270 | 连接数据库 271 | ``` 272 | cd walle-web/ 273 | vim config/local.php 274 | 275 | // 修改 PHP 数组 276 | 'db' => [ 277 | 'dsn' => 'mysql:host=127.0.0.1;dbname=walle', # 新建数据库walle 278 | 'username' => 'username', # 连接的用户名 279 | 'password' => 'password', # 连接的密码 280 | ], 281 | ``` 282 | 283 | 安装 vendor 284 | ``` 285 | cd /data/www/walle-web 286 | composer install --prefer-dist --no-dev --optimize-autoloader -vvvv 287 | ``` 288 | 289 | 初始化项目 290 | ``` 291 | cd /data/www/walle-web 292 | ./yii walle/setup 293 | ``` 294 | 295 | 配置 Nginx 296 | ``` 297 | server { 298 | listen 80; 299 | server_name walle.compony.com; # 改你的host 300 | root /the/dir/of/walle-web/web; # 根目录为web 301 | index index.php; 302 | 303 | # 建议放内网 304 | # allow 192.168.0.0/24; 305 | # deny all; 306 | 307 | location / { 308 | try_files $uri $uri/ /index.php$is_args$args; 309 | } 310 | 311 | location ~ \.php$ { 312 | try_files $uri = 404; 313 | fastcgi_pass 127.0.0.1:9000; 314 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 315 | include fastcgi_params; 316 | } 317 | } 318 | ``` 319 | 320 | 然后访问你配置好的域名或服务器地址,管理员默认账号密码是 `admin/admin`,开发者默认账号密码是 `demo/demo`。 321 | 322 | 有些错误 walle 捕捉不到,默认操作日志在 /tmp/walle/ 下,具体可在 config/local.php 里 log.dir 配置路径,tail 着日志,部署看日志。 323 | 324 | 如果发现 /tmp/walle 下没有日志,很有可能是因为 CentOs 7 yum 安装的 php-fpm 默认 /tmp 目录不可写:/usr/lib/systemd/system/php-fpm.service 中的 PrivateTmp=true 禁止了向tmp目录写日志,解决方法如下: 325 | ``` 326 | vi /usr/lib/systemd/system/php-fpm.service 327 | PrivateTmp=false 328 | 329 | systemctl daemon-reload 330 | systemctl reload php-fpm 331 | ``` 332 | 333 | ## Supervisor 334 | 335 | 安装 Supervisor 336 | ``` 337 | 338 | ``` 339 | 340 | ## Alias 常用进程命令 341 | 342 | - Nginx 343 | - alias nginx.start='systemctl start nginx' 344 | - alias nginx.restart='systemctl restart nginx' 345 | - alias nginx.stop='systemctl stop nginx' 346 | - alias nginx.status='systemctl status nginx' 347 | - Mysql 348 | - alias mysql.start='systemctl start mysql' 349 | - alias mysql.restart='systemctl restart mysql' 350 | - alias mysql.stop='systemctl stop mysql' 351 | - alias mysql.status='systemctl status mysql' 352 | - PHP-FPM 353 | - alias php-fpm.start='systemctl start php-fpm' 354 | - alias php-fpm.restart='systemctl restart php-fpm' 355 | - alias php-fpm.stop='systemctl stop php-fpm' 356 | - alias php-fpm.status='systemctl status php-fpm' 357 | - Redis 358 | - alias redis.start='systemctl start redis' 359 | - alias redis.restart='systemctl restart redis' 360 | - alias redis.stop='systemctl stop redis' 361 | - alias redis.status='systemctl status redis' 362 | - Beanstalkd 363 | - alias beanstalkd.start='systemctl start beanstalkd' 364 | - alias beanstalkd.restart='systemctl restart beanstalkd' 365 | - alias beanstalkd.stop='systemctl stop beanstalkd' 366 | - alias beanstalkd.status='systemctl status beanstalkd' 367 | - Gogs 368 | - alias gogs.start='/etc/rc.d/init.d/gogs start' 369 | - alias gogs.stop='/etc/rc.d/init.d/gogs stop' 370 | - alias gogs.restart='/etc/rc.d/init.d/gogs restart' 371 | - alias gogs.status='/etc/rc.d/init.d/gogs status' 372 | 373 | -------------------------------------------------------------------------------- /博客文章/与-MySQL-的零距离接触(二).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 与 MySQL 的零距离接触(二) 3 | date: 2016-09-09 20:14:34 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | 9 | ## 第 6 章 运算符和函数 10 | 11 | - ### 6-1 回顾和概述 12 | - 运算符 13 | - 函数 14 | - 字符函数 15 | - 数值运算符和函数 16 | - 比较运算符和函数 17 | - 日期时间函数 18 | - 信息函数 19 | - 聚合函数 20 | - 加密函数 21 | - ### 6-2 字符函数 22 | - CONCAT() - 字符连接 23 | - CONCAT_WS() - 使用指定的分隔符进行字符连接 24 | - FORMAT() - 数字格式化 25 | - LOWER() - 转换成小写字母 26 | - UPPER() - 转换成大写字母 27 | - LEFT() - 获取左侧字符 28 | - RIGHT() - 获取右侧字符 29 | - LENGTH() - 获取字符串长度 30 | - LTRIM() - 删除前导空格 31 | - RTRIM() - 删除后续空壳 32 | - TRIM() - 删除前导和后续空格 33 | - SUBSTRING() - 字符串截取 34 | - [NOT] LIKE - 模式匹配 35 | - %(百分号):代表任意个字符 36 | - _(下划线):代表任意一个字符 37 | - REPLACE() - 字符串替换 38 | - ### 6-3 数值运算符和函数 39 | - CEIL() - 进一取整 40 | - DIV - 整数除法 41 | - FLOOR() - 舍一取整 42 | - MOD - 取余数 43 | - POWER() - 幂运算 44 | - ROUND() - 四舍五入 45 | - TRUNCATE() - 数字截取 46 | - ### 6-4 比较运算符和函数 47 | - [NOT] BETWEEN...AND... - [不]在范围之内 48 | - [NOT] IN() - [不]在列出值范围内 49 | - IS [NOT] NULL - [不]为空 50 | - ### 6-5 日期时间函数 51 | - NOW() - 当前日期和时间 52 | - CURDATE() - 当前日期 53 | - CURTIME() - 当前时间 54 | - DATE_ADD() - 日期变化 55 | - DATEDIFF() - 日期差值 56 | - DATE_FORMAT() - 日期格式化 57 | - ### 6-6 信息函数 58 | - CONNECTION_ID() - 连接 ID 59 | - DATEBASE() - 当前数据库 60 | - LAST_INSERT_ID() - 最后插入记录的 ID 61 | - VERSION() - 最后插入记录的 ID 号 62 | - USER() - 当前用户 63 | - ### 6-7 聚合函数 64 | - AVG() - 平均值 65 | - COUNT() - 计数 66 | - MAX() - 最大值 67 | - MIN() - 最小值 68 | - SUM() - 求和 69 | - ### 6-8 加密函数 70 | - MD5() - 信息摘要算法 71 | - PASSWORD() - 密码 72 | 73 | ## 第 7 章 自定义函数 74 | 75 | - ### 7-1 回顾和概述 76 | - 略 77 | - ### 7-2 自定义函数简介 78 | - 自定义函数:用户自定义函数(user-defined function,UDF)是一种对 MySQL 扩展的途径,其用法与内置函数相同。 79 | - 自定义函数的两个必要条件: 80 | - 参数 81 | - 返回值 82 | - 函数可以接受任意类型的参数,也可以返回任意类型的值 83 | - 函数的参数与返回值没有任何必然、内在的联系 84 | - 创建自定义函数 85 | - `CREATE FUNCTION function_name RETURNS {STRING|INTEGER|REAL|DECIMAL} routine_body` 86 | - routine_body - 函数体 87 | - 函数体由合法的 SQL 语句构成 88 | - 函数体可以是简单的 SELECT 或 INSERT 语句 89 | - 函数体如果为复合结构则使用 BEGIN...END 语句 90 | - 复合结构可以包含声明,循环,控制结构 91 | - ### 7-3 创建不带参数的自定义函数 92 | - `create function f1() returns varchar(30) return date_format(now(),'%Y年%m月%d %H:%i:%s');` 93 | - 执行 `SELECT f1();`,输出 `2016年09月09 17:30:08` 94 | - ### 7-4 创建带有参数的自定义函数 95 | - `create function f2(num1 smallint unsigned,num2 smallint unsigned) returns float(10,2) unsigned return (num1+num2)/2;` 96 | - 执行 `SELECT f2(10,30);`,输出 `20.00` 97 | - ### 7-5 创建具有复合结构函数体的自定义函数 98 | - 重定义 SQL 语句结束分隔符 - `DELIMITER string` 99 | - `create function adduser(username varchar(20)) returns int unsigned begin insert test(username) values(username); return last_insert_id(); end //` 100 | 101 | ## 第 8 章 MySQL 存储过程 102 | 103 | - 8-1 课程回顾 104 | - 略 105 | - 8-2 存储过程简介 106 | - SQL 命令执行过程 107 | - SQL 命令 > MySQL 引擎 > 分析语法 > 可识别命令 > 执行 > 返回执行结果 108 | - 存储过程的简介 109 | - 存储过程是 SQL 语句和控制语句的预编译集合,以一个名称存储并作为一个单元处理 110 | - 存储过程的有点 111 | - 增强 SQL 语句的功能和灵活性 112 | - 实现较快的执行速度 113 | - 减少网络流量 114 | - 8-3 存储过程语法结构解析 115 | - 创建存储过程的语法结构 116 | - `CREATE` 117 | - `[DEFINER = {user | CURRENT_USER}]` 118 | - `PROCEDURE sp_name([proc_parameter[,...]])` 119 | - `[characteristic ...] routine_body` 120 | - `proc_parameter:` 121 | - `[IN | OUT | INOUT] param_name type ` 122 | - 创建存储过程的参数含义 123 | - IN,表示该参数的值必须在调用存储过程时指定 124 | - OUT,表示该参数的值可以被存储过程改变,并且可以返回 125 | - INOUT,表示该参数在调用时指定,并且可以被改变和返回 126 | - 创建存储过程的过程体 127 | - 过程体由合法的 SQL 语句构成 128 | - 过程体可以是任意 SQL 语句 129 | - 过程体如果为复合结构则使用 BEGIN...END 语句 130 | - 复合结构可以包含声明,循环,控制结构 131 | - 8-4 创建不带参数的存储过程 132 | - SQL 语句 - `CREATE PROCEDURE sp1() SELECT VERSION();` 133 | - 调用 - `CALL sp1();` 或 `CALL sp1` 134 | - 8-5 创建带有IN类型参数的存储过程 135 | - SQL 语句 - `CREATE PROCEDURE removeUserById(IN user_id INT UNSIGNED) BEGIN DELETE FROM users WHERE id = user_id; END //` 136 | - 调用 - `CALL removeUserById(22);` 137 | - 8-6 创建带有IN和OUT类型参数的存储过程 138 | - SQL 语句 - `CREATE PROCEDURE removeUserAndReturnUserNums(IN user_id INT UNSIGNED,OUT user_nums INT UNSIGNED) BEGIN DELETE FROM users WHERE id = user_id; SELECT cout(id) FROM users INTO user_nums; END //` 139 | - 调用 - `CALL removeUserAndReturnUserNums(27,@user_nums); SELECT @user_nums` 140 | - `@name` 声明用户变量,只能是当前用户使用 141 | - 8-7 创建带有多个OUT类型参数的存储过程 142 | - SQL 语句 - `CREATE PROCEDURE removeUserByAgeAndReturnInfos(IN user_age SMALLINT UNSIGNED,OUT deleteUsers SMALLINT UNSIGNED,OUT user_counts SMALLINT UNSIGNED) BEGIN DELETE FROM users WHERE age = user_age; SELECT ROW_COUNT() INTO deleteUsers; SELECT COUNT(id) FROM users INTO user_counts; END //` 143 | - 调用 - `CALL removeUserByAgeAndReturnInfos(20,@a,@b)` 144 | - 8-8 存储过程与自定义函数的区别 145 | - 存储过程实现的功能要复杂一些;而函数的针对性更强 146 | - 存储过程可以返回多个值;函数只能有一个返回值 147 | - 存储过程一般都是独立执行;而函数可以作为其他 SQL 语句的组成部分来出现。 148 | 149 | ## 第 9 章 MySQL 存储引擎 150 | 151 | - 9-1 课程回顾 152 | - 略 153 | - 9-2 存储引擎简介 154 | - 简介:MySQL 可以将数据以不同的技术存储在文件(内存)中,这种技术就成为存储引擎。每一种存储引擎使用不同的存储机制、索引技巧、锁定水平,最终提供广泛且不同的功能。 155 | - 类型: 156 | - MyISAM 157 | - InnoDB 158 | - Memory 159 | - CSV 160 | - Archive 161 | - 9-3 相关知识点之并发处理 162 | - 并发控制:当多个连接对记录进行修改时保证数据的一致性和完整性 163 | - 锁 164 | - 共享锁(读锁):在同一时间段内,多个用户可以读取同一个资源,读取过程中数据不会发生任何变化。 165 | - 排它锁(写锁):在任何时候只能有一个用户写入资源,当进行写锁时会阻塞其他的读锁或者写锁操作。 166 | - 锁颗粒 167 | - 表锁,是一种开销最小的锁策略 168 | - 行锁,是一种开销最大的锁策略 169 | - 9-4 相关知识点之事务处理 170 | - 事务 171 | - 事务用于保证数据库的完整性 172 | - 9-5 相关知识点之外键和索引 173 | - 外键 174 | - 外键是保证数据一致性的策略 175 | - 索引 176 | - 是对数据表中一列或多列的值进行排序的一种结构。 177 | - 9-6 各个存储引擎特点 178 | 179 | 特性/引擎|MyISAM | Aria | InnoDB | XtraDB | Memory | Archive| 180 | ---|---|---|---|---|---|--- 181 | 数据存储|传统顺序数据储存 |传统顺序数据储存|表空间存储方式|表空间存储方式||| 182 | 事务支持|不支持 | 支持|支持|支持|不支持|不支持| 183 | 外键|不支持 | 不支持|支持|支持|不支持|不支持| 184 | 全文检索|支持 | 支持|5.6 之后支持|5.6 之后支持||| 185 | 锁级别|表级锁 | 表级锁|行级锁|行级锁|表级锁|行级锁| 186 | Count 速度|快 | 快|慢|慢||| 187 | 适合业务|读多写少、单表数据量小于 1 KW |读多写少、单表数据量小于 1 KW|读写均衡、数据量不限|读写均衡、数据量不限||| 188 | 可用版本|MySQL、MariaDB、Percona | MariaDB|MySQL、MariaDB、Percona|MariaDB、Percona||| 189 | 其他说明|传统顺序索引数据库,适合读多写少小数据量业务 | MyISAM 增强版,性能更好|适合高压力高性能的业务模型|InnoDB 增强版||| 190 | 191 | - 9-7 设置存储引擎 192 | - 修改搜索引擎的方法,主要有两种: 193 | - 通过修改 MySQL 配置文件实现:`default-storage-engine = engine` 194 | - 通过创建数据表命令实现 195 | - `CREATE TABLE table_name(...,...) ENGINE = engine;` 196 | - 通过修改数据表命令实现 197 | - `ALTER TABLE table_name ENGINE [=] engine_name;` 198 | 199 | ## 第 10 章 MySQL 图形化管理工具 200 | 201 | - 10-1 课程介绍 202 | - PHPMyAdmin 203 | - Navicat 204 | - MySQL Workbench 205 | - 10-2 管理工具之 phpMyAdmin 206 | - 10-3 管理工具之 Navicat for MySQL 207 | - 10-4 管理工具之 MySQL Workbench 208 | 209 | 210 | -------------------------------------------------------------------------------- /博客文章/使用-Laravel-与-Vue-js-进行微信开发(三).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 与 Vue.js 进行微信开发(三) 3 | date: 2017-01-16 23:04:33 4 | category: PHP 5 | tags: Laravel 6 | --- 7 | 8 | ## 目标 9 | 10 | 本文的目标是实现微信公众号菜单的创建与删除 11 | 12 | 13 | ## 创建路由 14 | 15 | ```php 16 | /* 微信菜单创建 */ 17 | Route::get('/create-menus', [ 18 | 'as' => 'wechat.create.menus', 19 | 'uses' => 'WeChatServerController@createMenus', 20 | ]); 21 | 22 | /* 微信菜单创建 */ 23 | Route::get('/destroy-menus', [ 24 | 'as' => 'wechat.destroy.menus', 25 | 'uses' => 'WeChatServerController@destroyMenus', 26 | ]); 27 | ``` 28 | 29 | ## 修改控制器 30 | 31 | 修改控制器 app/Http/Controllers/WeChat/WeChatServerController.php,新增代码如下: 32 | ``` 33 | /** 34 | * 创建新的微信菜单 35 | * @param WeChat $weChat 36 | * @return string 37 | */ 38 | public function createMenus(WeChat $weChat) 39 | { 40 | $menu = $weChat->menu; 41 | $buttons = [ 42 | [ 43 | "type" => "view", 44 | "name" => "进入好答", 45 | "url" => route('wechat.index'), 46 | ], 47 | [ 48 | "type" => "click", 49 | "name" => "听我唱歌", 50 | "key" => "K0001_LET_ME_SING_MY_SONG", 51 | ], 52 | ]; 53 | 54 | $json = $menu->add($buttons); 55 | $result = json_decode($json, true); 56 | 57 | if ($result['errcode'] == 0 && $result['errmsg'] == 'ok') { 58 | echo "创建菜单成功"; 59 | } else { 60 | echo "创建菜单失败"; 61 | } 62 | } 63 | 64 | /** 65 | * 删除所有微信菜单 66 | * @param WeChat $weChat 67 | * @return string 68 | */ 69 | public function destroyMenus(WeChat $weChat) 70 | { 71 | $result = $weChat->menu->destroy(); 72 | 73 | if ($result['errcode'] == 0 && $result['errmsg'] == 'ok') { 74 | echo "删除菜单成功"; 75 | } else { 76 | echo "删除菜单失败"; 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /博客文章/使用-Laravel-与-Vue-js-进行微信开发(二).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 与 Vue.js 进行微信开发(二) 3 | date: 2017-01-16 20:42:22 4 | category: PHP 5 | tags: Laravel 6 | --- 7 | 8 | ## 目标 9 | 10 | 本文的目标是实现微信用户授权,当用户进入 Web App 的时候,能够获取用户的信息。 11 | 12 | 13 | ## 配置回调地址 14 | 15 | 微信公众平台 -> 开发者工具 -> 公众平台测试帐号 -> 测试号管理 -> 体验接口权限表 -> 功能服务 -> 网页帐号 -> 修改 -> 填写回调域名,格式如:www.baidu.com 16 | 17 | ## 修改配置文件 18 | 可以修改文件 config/wechat.php ,也可以在 .env 文件进行配置 19 | 20 | wechat.php 文件配置 21 | ``` 22 | /* 23 | * OAuth 配置 24 | * 25 | * only_wechat_browser: 只在微信浏览器跳转 26 | * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login 27 | * callback:OAuth授权完成后的回调页地址(如果使用中间件,则随便填写。。。) 28 | */ 29 | 'oauth' => [ 30 | 'only_wechat_browser' => false, 31 | 'scopes' => array_map('trim', explode(',', env('WECHAT_OAUTH_SCOPES', 'snsapi_userinfo'))), 32 | 'callback' => env('WECHAT_OAUTH_CALLBACK', '/examples/oauth_callback.php'), 33 | ], 34 | ``` 35 | 36 | .env 文件配置 37 | ``` 38 | WECHAT_APPID=**** 39 | WECHAT_SECRET=*** 40 | WECHAT_TOKEN=**** 41 | WECHAT_AES_KEY= 42 | 43 | WECHAT_OAUTH_SCOPES=snsapi_userinfo 44 | WECHAT_OAUTH_CALLBACK=/wechat/oauth-callback 45 | ``` 46 | 47 | ## 新增路由 48 | 49 | 修改文件 app/Http/Routes/wechat.php,代码如下: 50 | ```php 51 | <?php 52 | /* 处理微信发送消息 */ 53 | Route::any('/request-handler', 'WeChatServerController@requestHandler'); 54 | 55 | /* 处理微信授权回调 */ 56 | Route::any('/oauth-callback', 'WeChatServerController@oauthCallback'); 57 | 58 | /* 微信应用首页 */ 59 | Route::get('/', [ 60 | 'as' => 'wechat.index', 61 | 'uses' => 'WeChatApplicationController@index', 62 | 'middleware' => [], 63 | ]); 64 | ``` 65 | 66 | ## 关闭路由 CSRF 67 | 修改文件 app/Http/Middleware/VerifyCSsrfToken.php,代码如下: 68 | ```php 69 | protected $except = [ 70 | '/wechat/request-handler', 71 | '/wechat/oauth-callback' 72 | ]; 73 | ``` 74 | 75 | 76 | ## 获取授权 77 | 78 | ### 1.授权页面 79 | 80 | 新增文件 app/Http/Controllers/WeChat/WeChatApplicationController.php,代码如下: 81 | ```php 82 | <?php 83 | 84 | namespace App\Http\Controllers\WeChat; 85 | 86 | use Illuminate\Http\Request; 87 | use App\Http\Controllers\Controller; 88 | use EasyWeChat\Foundation\Application as WeChat; 89 | 90 | class WeChatApplicationController extends Controller 91 | { 92 | public function index(Request $request, WeChat $wechat) 93 | { 94 | // 如果 Session 中没有用户信息,则跳转至微信授权页面 95 | if (empty($request->session()->has('weChatUserInfo'))) { 96 | $oauth = $wechat->oauth; 97 | 98 | return $oauth->redirect(); 99 | } 100 | $user = $request->session()->get('weChatUserInfo'); 101 | dump($user); 102 | } 103 | } 104 | ``` 105 | 106 | ### 2.回调操作 107 | 108 | 修改文件 app/Http/Controllers/WeChat/WeChatServerController.php,代码如下: 109 | ```php 110 | <?php 111 | /** 112 | * 微信用户授权回调 113 | * @param Request $request 114 | * @param WeChat $weChat 115 | * @return \Illuminate\Http\RedirectResponse 116 | */ 117 | public function oauthCallback(Request $request, WeChat $weChat) 118 | { 119 | $oauth = $weChat->oauth; 120 | $user = $oauth->user(); 121 | $request->session()->put('weChatUserInfo', $user->toArray()); 122 | 123 | return redirect()->route('wechat.index'); 124 | } 125 | ``` 126 | 127 | ### 3.访问首页 128 | 129 | 下载微信的 Web 开发者工具,在工具中访问授权首页,比如访问:你的域名/wechat,可以跳转至授权页,如下图: 130 | ![](http://o93kt6djh.bkt.clouddn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-01-16%2022.24.46.png) 131 | ![](http://o93kt6djh.bkt.clouddn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-01-16%2022.25.06.png) 132 | -------------------------------------------------------------------------------- /博客文章/使用-Laravel-与-Vue.js-进行微信开发(一).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 与 Vue.js 进行微信开发(一) 3 | date: 2017-01-15 09:11:57 4 | category: PHP 5 | tags: Laravel 6 | --- 7 | 8 | ## 目标 9 | 10 | 本文的目标是实现与微信服务器的通信。 11 | 12 | ## 环境 13 | 14 | - MacOS 15 | - Laravel 5.1 16 | - Vue.js 2.1 17 | - EasyWeChat 18 | 19 | ## 安装 EasyWechat 20 | 21 | ``` 22 | composer require "overtrue/laravel-wechat:~3.0" 23 | ``` 24 | 25 | ## 配置 26 | 27 | - 注册 ServiceProvider: 28 | - Overtrue\LaravelWechat\ServiceProvider::class, 29 | - 创建配置文件 30 | - php artisan vendor:publish 31 | - 修改 config/wechat.php 32 | - app_id:微信测试号 appID 33 | - secret:微信测试号 appsecret 34 | - token:自定义,要与微信测试号填写的 Token 保持一致 35 | - aes_key:加密信息用的,本文采用明文的方式,暂不填写 36 | 37 | ## 创建微信分组路由 38 | 39 | 修改文件 app/Http/routes.php,代码如下: 40 | ```php 41 | Route::group([ 42 | 'prefix' => 'wechat', 43 | 'namespace' => 'WeChat', 44 | 'middleware' => [], 45 | ], function() { 46 | require_once __DIR__ . '/Routes/wechat.php'; 47 | }); 48 | ``` 49 | 50 | 新增文件 app/Http/Routes/wechat.php,代码如下: 51 | ```php 52 | Route::any('/request-handler', 'WeChatServerController@requestHandler'); 53 | ``` 54 | 55 | ## 关闭路由 CSRF 56 | 修改文件 app/Http/Middleware/VerifyCSsrfToken.php,代码如下: 57 | ```php 58 | protected $except = [ 59 | '/wechat/request-handler' 60 | ]; 61 | ``` 62 | 63 | ## 创建控制器 64 | 65 | 新增文件 app/Http/Controllers/WeChat/WeChatServerController.php,代码如下: 66 | ```php 67 | <?php 68 | 69 | namespace App\Http\Controllers\WeChat; 70 | 71 | use Illuminate\Http\Request; 72 | use App\Http\Controllers\Controller; 73 | use EasyWeChat\Foundation\Application as WeChat; 74 | 75 | class WeChatServerController extends Controller 76 | { 77 | /** 78 | * 处理微信的请求消息 79 | * @return string 80 | */ 81 | public function requestHandler(WeChat $weChat) 82 | { 83 | $weChat->server->setMessageHandler(function($message) { 84 | return "欢迎关注 {$message}!"; 85 | }); 86 | 87 | return $weChat->server->serve(); 88 | } 89 | } 90 | ``` 91 | 92 | ## 验证 93 | 94 | - 配置微信公众平台测试号 95 | - 接口配置信息 96 | - URL:域名/wechat 97 | - Token:自定义,要与 wechat.php 文件的 token 一致 98 | - JS 接口安全域名 99 | - 填写域名,例如 baidu.com ,不需要写 www 和 http[s]:// 100 | - 测试号二维码 101 | - 添加测试用户 102 | 103 | URL 和 Token 通过验证之后,关注微信公众平台测试号,并发送测试消息,成功的话,如下图: 104 | ![](http://o93kt6djh.bkt.clouddn.com/yanzhengwechatmessage.jpeg) 105 | 106 | -------------------------------------------------------------------------------- /博客文章/使用-Laravel-编写自定义命令.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 编写自定义命令 3 | date: 2016-08-25 21:48:36 4 | tags: Laravel 5 | category: PHP 6 | --- 7 | 8 | 本文讲述了如何使用 Laravel 开发命令行 9 | 10 | ## 环境 11 | 12 | - PHP 7 13 | - Laravel 5.1 14 | - OS X El Capitan 10.11.4 15 | 16 | ## 简介 17 | 18 | Artisan 是 Laravel 的命令行接口的名称,它提供了许多实用的命令来帮助你开发 Laravel 应用,它由强大的 Symfony Console 组件所驱动。 19 | 20 | ## 文档 21 | 22 | [Artisan](http://laravel-china.org/docs/5.1/artisan) 23 | 24 | 25 | ## 目的 26 | 27 | 本文目的旨在实现三个简单的命令,分别是优化项目、清除缓存和发送邮件 28 | 29 | 30 | ## 实现步骤 31 | 32 | ### 新建命令 33 | ``` 34 | php artisan make:console LaravelOptimize --command=laravel:optimize 35 | php artisan make:console LaravelClean --command=laravel:clean 36 | php artisan make:console SendEmails --command=emails:send 37 | ``` 38 | 39 | ### 注册命令 40 | 打开文件 `app/Console/Kernel.php`,修改代码如下: 41 | ``` 42 | protected $commands = [ 43 | \App\Console\Commands\Inspire::class, 44 | \App\Console\Commands\SendEmails::class, 45 | \App\Console\Commands\LaravelClean::class, 46 | \App\Console\Commands\LaravelOptimize::class, 47 | ]; 48 | ``` 49 | 50 | ### 编写命令执行代码 51 | 52 | 文件 `SendEmails.php` 代码如下: 53 | ``` 54 | <?php 55 | 56 | namespace App\Console\Commands; 57 | 58 | use App\Facades\UserRepository; 59 | use Illuminate\Console\Command; 60 | use Mail; 61 | 62 | class SendEmails extends Command 63 | { 64 | /** 65 | * The name and signature of the console command. 66 | * 67 | * @var string 68 | */ 69 | protected $signature = 'emails:send'; 70 | 71 | /** 72 | * The console command description. 73 | * 74 | * @var string 75 | */ 76 | protected $description = 'use SendCloud to send emails'; 77 | 78 | /** 79 | * Create a new command instance. 80 | * 81 | * @return void 82 | */ 83 | public function __construct() 84 | { 85 | parent::__construct(); 86 | } 87 | 88 | /** 89 | * Execute the console command. 90 | * 91 | * @return mixed 92 | */ 93 | public function handle() 94 | { 95 | $this->line("即将执行邮件发送命令"); 96 | 97 | if($this->confirm('确定执行?')){ 98 | $users = UserRepository::all(); 99 | 100 | $bar = $this->output->createProgressBar(count($users)); 101 | 102 | foreach ($users as $user) { 103 | $icon = "http://o93kt6djh.bkt.clouddn.com/Laravel-SendEmaillaravel-200x50.png"; 104 | $image = "http://o93kt6djh.bkt.clouddn.com/Laravel-SendEmaillaravel-600x300.jpg"; 105 | 106 | Mail::send('emails.test-image', 107 | [ 108 | 'name' => $user->name, 109 | 'icon' => $icon, 110 | 'image' => $image, 111 | ], 112 | function ($email) use ($user) { 113 | $email->to($user->email)->subject('图文邮件标题'); 114 | }); 115 | 116 | $bar->advance(); 117 | } 118 | 119 | $bar->finish(); 120 | 121 | $this->info("发送邮件完毕"); 122 | } else { 123 | $this->info("取消执行邮件发送命令"); 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | 文件 `LaravelOptimize.php` 代码如下: 130 | ``` 131 | <?php 132 | 133 | namespace App\Console\Commands; 134 | 135 | use Illuminate\Console\Command; 136 | use Illuminate\Support\Facades\Artisan; 137 | 138 | class LaravelOptimize extends Command 139 | { 140 | /** 141 | * The name and signature of the console command. 142 | * 143 | * @var string 144 | */ 145 | protected $signature = 'laravel:optimize'; 146 | 147 | /** 148 | * The console command description. 149 | * 150 | * @var string 151 | */ 152 | protected $description = 'optimize laravel project'; 153 | 154 | /** 155 | * Create a new command instance. 156 | * 157 | * @return void 158 | */ 159 | public function __construct() 160 | { 161 | parent::__construct(); 162 | } 163 | 164 | /** 165 | * Execute the console command. 166 | * 167 | * @return mixed 168 | */ 169 | public function handle() 170 | { 171 | $this->line("即将执行优化缓存命令"); 172 | if($this->confirm('确定执行?')){ 173 | $this->line("开始执行缓存命令"); 174 | 175 | Artisan::call("config:cache"); 176 | $this->info('Configuration cache cleared!'); 177 | $this->info('Configuration cached successfully!'); 178 | 179 | Artisan::call("route:cache"); 180 | $this->info('Route cache cleared!'); 181 | $this->info('Routes cached successfully!'); 182 | 183 | Artisan::call("optimize"); 184 | $this->info('Generating optimized class loader'); 185 | 186 | if(function_exists('exec')){ 187 | exec("composer dump-autoload"); 188 | } 189 | 190 | Artisan::call("ide-helper:generate"); 191 | $this->info('A new helper file was written to _ide_helper.php'); 192 | 193 | $this->info("优化缓存成功"); 194 | } else { 195 | $this->info("取消执行优化缓存命令"); 196 | } 197 | } 198 | } 199 | ``` 200 | 201 | 文件 `LaravelClean.php` 代码如下: 202 | ``` 203 | <?php 204 | 205 | namespace App\Console\Commands; 206 | 207 | use Illuminate\Console\Command; 208 | use Illuminate\Support\Facades\Artisan; 209 | 210 | class LaravelClean extends Command 211 | { 212 | /** 213 | * The name and signature of the console command. 214 | * 215 | * @var string 216 | */ 217 | protected $signature = 'laravel:clean'; 218 | 219 | /** 220 | * The console command description. 221 | * 222 | * @var string 223 | */ 224 | protected $description = 'clean project all cache'; 225 | 226 | /** 227 | * Create a new command instance. 228 | * 229 | * @return void 230 | */ 231 | public function __construct() 232 | { 233 | parent::__construct(); 234 | } 235 | 236 | /** 237 | * Execute the console command. 238 | * 239 | * @return mixed 240 | */ 241 | public function handle() 242 | { 243 | $this->line("即将执行清除缓存命令"); 244 | if($this->confirm('确定执行?')){ 245 | $this->line("开始执行缓存命令"); 246 | 247 | Artisan::call("clear-compiled"); 248 | 249 | Artisan::call("auth:clear-resets"); 250 | $this->info('Expired reset tokens cleared!'); 251 | 252 | Artisan::call("cache:clear"); 253 | $this->info('Application cache cleared!'); 254 | 255 | Artisan::call("config:clear"); 256 | $this->info('Configuration cache cleared!'); 257 | 258 | Artisan::call("debugbar:clear"); 259 | $this->info('Debugbar Storage cleared!'); 260 | 261 | Artisan::call("route:clear"); 262 | $this->info('Route cache cleared!'); 263 | 264 | Artisan::call("view:clear"); 265 | $this->info('Compiled views cleared!'); 266 | 267 | $this->info("清除缓存成功"); 268 | } else { 269 | $this->info("取消执行清除缓存命令"); 270 | } 271 | } 272 | } 273 | ``` 274 | 275 | ## 执行效果 276 | 277 | 在终端分别执行以下代码: 278 | ``` 279 | php artisan laravel:optimize 280 | php artisan laravel:clean 281 | php artisan emails:send 282 | ``` 283 | 284 | 执行效果分别如下显示: 285 | 286 | #### php artisan laravel:optimize 287 | ![执行缓存清楚](http://o93kt6djh.bkt.clouddn.com/laravel-optimize.png) 288 | 289 | #### php artisan laravel:clean 290 | ![执行缓存清楚](http://o93kt6djh.bkt.clouddn.com/laravel-clean.png) 291 | 292 | #### php artisan emails:send 293 | ![执行邮件发送](http://o93kt6djh.bkt.clouddn.com/email-send.png) 294 | 295 | 296 | -------------------------------------------------------------------------------- /博客文章/使用-PHPStorm-与-Xdebug-调试-Laravel-一.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 PHPStorm 与 Xdebug 调试 Laravel (一) 3 | date: 2016-06-20 21:38:32 4 | tags: PHPStorm 5 | category: Tooling 6 | --- 7 | 8 | 本文的目的学会使用除了 `var_dump,echo,printf` 的调试方法 9 | 10 | ## 环境 11 | 12 | - 系统版本:OSX 10.11.4 13 | - PHP 版本:7.0.5 14 | - Xdebug 版本:2.4.0 15 | - Laravel 版本:5.1.31 16 | - PHPStorm 版本:10.0.4 17 | 18 | 19 | ## Xdebug 配置 20 | 21 | 本机的 Xdebug 配置文件位于 `/usr/local/etc/php/7.0/conf.d/ext-xdebug.ini` 22 | 23 | 打开文件添加并以下代码: 24 | ```php 25 | [xdebug] 26 | zend_extension="/usr/local/Cellar/php70-xdebug/2.4.0/xdebug.so" 27 | xdebug.idekey=PHPSTORM 28 | xdebug.remote_enable=1 29 | xdebug.remote_host=localhost 30 | xdebug.remote_port=10000 31 | xdebug.profiler_enable=1 32 | xdebug.profiler_output_dir="/Users/LuisEdware/Downloads/Xdebug" 33 | ``` 34 | 35 | ## PHPStorm 配置 36 | 37 | 打开 PHPStorm,首先配置 PHP 的使用版本与 Interpreter 38 | `Preferences => Language & Frameworks -> PHP` 39 | ![配置 PHP 的使用版本](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E8%AE%BE%E7%BD%AE%20PHP%20%E7%89%88%E6%9C%AC.png "PHP 版本.png") 40 | 41 | - PHP language level :选择 PHP 的使用版本 42 | - Interpreter : 配置 PHP 可执行文件的位置 43 | 44 | ![配置 PHP 可执行文件的位置](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E8%AE%BE%E7%BD%AE%20Interpreter.png "Interpreter.png") 45 | 46 | - Name : 命名 47 | - PHP executable : PHP 可执行文件位置,本机使用 Homebrew 安装的 PHP,位置在`/usr/local/Cellar/php70/7.0.5/bin/php` 48 | 49 | 50 | 然后配置 PHP Debug 时的端口,将端口 `9000` 修改成 `10000` 51 | ![配置 Debug 端口](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E8%AE%BE%E7%BD%AE%20Xdebug.png "Xdebug.png") 52 | 53 | 接着修改 `Run => Edit configurations`,点击弹出窗口左上角加号,新增一个 `PHP Web Application` 54 | ![Run => Edit configurations](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-applicationRun%20Debug.png "Run Debug.png") 55 | ![PHP Web Application](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E6%96%B0%E5%BB%BA%20Web%20Application.png "Server.png") 56 | 57 | - Name : 命名 58 | - Server : 服务器,没有跟着下图创建 59 | - Start URL : 要开始 Debug 的 URL 60 | 61 | 跟随着选项新增一个 `Server` 62 | ![Server](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E6%96%B0%E5%BB%BA%20Server.png "Server.png") 63 | 64 | - Name : 命名 65 | - Host : 主机,我在本地将需要 Debug 的项目映射到 `cowcat.app` 上 66 | - Port : 端口 67 | - Debugger : 除了 Xdebug 还有 Zend Debugger,选择 Xdebug 68 | 69 | 设置断点,运行`Run => Debug 'Cowcat'` 70 | ![设置断点](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-application%E8%AE%BE%E7%BD%AE%E6%96%AD%E7%82%B9.png "Server.png") 71 | ![Debug Cowcat](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-applicationRun%20Debug%20Cowcat.png "Server.png") 72 | 73 | 74 | 当浏览器运行指定 URL(就是 PHP Web Application 配置时的 Start URL) 时,出现 Xdebug 控制台,根据控制台的信息和操作进行 Debug 75 | ![Xdebug 控制台](http://o93kt6djh.bkt.clouddn.com/phpstorm-debug-web-applicationDebugger%20Console.png "Server.png") 76 | 77 | 控制台的功能介绍如下: 78 | 79 | - 左侧绿色三角形 : `Resume Program`,表示將继续执行,直到下一个中断点停止。 80 | - 左侧红色方形 : `Stop`,表示中断当前程序调试。 81 | - 上方第一个图形示 : `Step Over`,跳过当前函数。 82 | - 上方第二个图形示 : `Step Into`,进入当前函数內部的程序(相当于观察程序一步一步执行)。 83 | - 上方第三个图形示 : `Force Step Into`,強制进入当前函数內部的程序。 84 | - 上方第四个图形示 : `Step Out`,跳出当前函数內部的程式。 85 | - 上方第五个图形示 : `Run to Cursor`,定位到当前光标。 86 | - Variables : 可以观察到所有全局变量、当前局部变量的数值 87 | - Watches : 可以新增变量,观察变量随着程序执行的变化。 88 | 89 | -------------------------------------------------------------------------------- /博客文章/使用-PHPStorm-与-Xdebug-调试-Laravel-二.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 PHPStorm 与 Xdebug 调试 Laravel (二) 3 | date: 2016-06-27 21:36:54 4 | tags: PHPStorm 5 | category: Tooling 6 | --- 7 | 8 | 本文的目的学会使用除了 `var_dump,echo,printf` 的调试方法 9 | 10 | ## 环境 11 | 12 | - 系统版本:OSX 10.11.4 13 | - PHP 版本:7.0.5 14 | - Xdebug 版本:2.4.0 15 | - Laravel 版本:5.1.31 16 | - PHPStorm 版本:10.0.4 17 | 18 | 19 | 根据上篇文章的配置,在工作时会发现,我们需要经常调整 `PHP Web Application` 的 URL 进行 Debug。 20 | 21 | 举个例子,假如想要 `Debug` 菜单列表,我需要修改成 `/menu/`,如果想要 `Debug` 新增菜单页面,我需要修改成 `/menu/create`。 22 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationPHP%20Web%20Application.png) 23 | 这样进行 `Debug` 的过程十分烦琐,所以需要更加友好的操作方式,以便加快工作效率。 24 | 25 | 26 | ## PHPStorm 配置 27 | 28 | 打开 PHPStorm,打开配置面板 29 | `Preferences => Language & Frameworks -> PHP -> Debug`。 30 | 31 | 点击蓝色链接 `Use debugger bookmarklets to initiate debugging from your favorite browser`。 32 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationPHP%20Debug%20Configuration.png) 33 | 34 | 点击页面左下角的蓝色按钮,生成 PHPStorm Debug 的专属书签。 35 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationPHPStorm%20Debug%20Config.png) 36 | 37 | 然后将生成好的 `DEBUG` 书签`Start debugger`、`Stop debugger`、`Debug this page` 拖动保存到浏览器的书签栏中,方便随时进行 `Debug`。 38 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationBookMark.png) 39 | 40 | 监听浏览器的 `Debug` 操作,`Run -> Start Listening for PHP Debug Connections` 41 | 42 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationListening%20PHP%20Debug.png) 43 | 44 | 然后在浏览器输入想要进行 `Debug` 的页面,然后点击书签栏的 `Start debugger`,刷新页面,就能在 `PHPStorm` 里面看见 `Debug` 的控制台了。 45 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationDebug%20Page2.png) 46 | ![](http://o93kt6djh.bkt.clouddn.com/2-phpstorm-debug-web-applicationDebug%20Page.png) 47 | 48 | PHPStorm 的 Debug 方式不仅仅局限于 Laravel 框架,同样适用于 ThinkPHP 与其他框架,也适用于原生的 PHP 代码。 49 | 学会使用这种方式之后,一般很少使用 `echo`,`var_dump`,`dd()`,`dump()`等原生或框架辅助函数进行 `Debug` 了。 50 | 51 | 52 | -------------------------------------------------------------------------------- /博客文章/使用-PHPStorm-的-Live-Templates-封装常用代码.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 PHPStorm 的 Live Templates 封装常用代码 3 | date: 2016-11-19 15:11:46 4 | tags: 5 | --- 6 | 7 | ## PHP Template Group 8 | 9 | ### fore 10 | ``` 11 | foreach ($ITERABLE$ as $VAR_VALUE$) { 12 | $END$ 13 | } 14 | ``` 15 | 16 | ### forek 17 | ``` 18 | foreach ($ITERABLE$ as $VAR_KEY$ => $VAR_VALUE$) { 19 | $END$ 20 | } 21 | ``` 22 | 23 | ### prif 24 | ``` 25 | private function $NAME$($PARAMETERS$){ 26 | $END$ 27 | } 28 | ``` 29 | 30 | ### prisf 31 | ``` 32 | private static function $NAME$($PARAMETERS$){ 33 | $END$ 34 | } 35 | ``` 36 | 37 | ### prof 38 | ``` 39 | protected function $NAME$($PARAMETERS$){ 40 | $END$ 41 | } 42 | ``` 43 | 44 | ### prosf 45 | ``` 46 | protected static function $NAME$($PARAMETERS$){ 47 | $END$ 48 | } 49 | ``` 50 | 51 | ### pubf 52 | ``` 53 | public function $NAME$($PARAMETERS$){ 54 | $END$ 55 | } 56 | ``` 57 | 58 | ### pubsf 59 | ``` 60 | public static function $NAME$($PARAMETERS$){ 61 | $END$ 62 | } 63 | ``` 64 | 65 | ### thr 66 | ``` 67 | throw new $END$ 68 | ``` 69 | 70 | ### tryc 71 | ``` 72 | try { 73 | $ITERABLE$ 74 | } catch (\$Exception$ $e) { 75 | $END$ 76 | } 77 | ``` 78 | 79 | ## 80 | -------------------------------------------------------------------------------- /博客文章/使用-Sublime-Text-3-开发-PHP-的总结.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Sublime Text 3 开发 PHP 的心得 3 | date: 2016-03-13 22:34:20 4 | tags: Sublime Text 3 5 | category: Tooling 6 | --- 7 | 8 | PHPStorm 不卡之日,SublimeText 舍弃之时 9 | 10 | ## 环境 11 | 12 | - 系统:OSX Yosemite 10.10.5 13 | - 版本:Sublime Text 3 Build 3103 14 | 15 | 16 | ## 安装 17 | 18 | 使用 Homebrew 安装 Sublime Text 3 19 | ```shell 20 | brew install Caskroom/cask/sublime-text 21 | ``` 22 | 23 | ## 终端配置 24 | 25 | 执行以下代码,即可在终端使用 `Sublime Text 3` 26 | 27 | ``` 28 | // 创建程序链接 29 | ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" /usr/local/bin/subl 30 | 31 | // 查看使用帮助 32 | subl -h 33 | ``` 34 | 35 | 36 | 37 | ## 快捷键 38 | 39 | ### 常用 40 | 41 | |按键|命令| 42 | |--|---| 43 | |⌘ + C|复制| 44 | |⌘ + ⇧ + D|复制粘贴| 45 | |⌘ + X|剪切| 46 | |⌘ + V|粘贴| 47 | |⌘ + ⇧ + V|保持缩进粘贴| 48 | |⌘ + ⌥ + V|打开粘贴面板| 49 | |⌘ + F|查找| 50 | |⌘ + /|注释行| 51 | |⌘ + ⌥ + /|生成注释| 52 | |⌘ + ⇧ + F|全局查找、替换| 53 | |⌘ + ↩|在光标下插入行| 54 | |⌘ + ⇧ + ↩|在光标上插入行| 55 | |⌘ + ⇧ + t|打开关闭的标签页| 56 | |⌘ + [NUM]|选取对应数字的标签页| 57 | |⌘ + ⇧ + [|上一个标签页| 58 | |⌘ + ⇧ + ]|下一个标签页| 59 | |⌃ + ⇧ + ↑|向上扩展光标| 60 | |⌃ + ⇧ + ↓|向下扩展光标| 61 | 62 | ### 书签 63 | 64 | |按键|命令| 65 | |--|---| 66 | |⌘ + F2|生成或撤销书签| 67 | |⇧ + F2|上一个书签| 68 | |F2|下一个书签| 69 | 70 | 71 | ### 删除 72 | 73 | |按键|命令| 74 | |--|---| 75 | |⌃ + ⇧ + K|删除当前行| 76 | |⌘ + K + ⌫| 删除光标之后的内容| 77 | |⌘ + K + ⌘ + K|删除光标之后的内容| 78 | 79 | ### 跳转 80 | 81 | |按键|命令| 82 | |--|---| 83 | |⌘ + P , ⌘ + T|跳转到文件| 84 | |⌘ + R|跳转到方法| 85 | |⌃ + G|跳转到行数| 86 | |⌃ + ⇧ + P|切换工作空间| 87 | |⌘ + ⇧ + P|打开命令面板| 88 | |⌃ + -|向后跳转至修改处| 89 | |⌃ + ⇧ + -|向前跳转至修改处| 90 | 91 | 92 | ### 光标移动 93 | 94 | |按键|命令| 95 | |--|---| 96 | |⌃ + P|光标向上移动| 97 | |⌃ + N|光标向下移动| 98 | |⌃ + B|光标向左移动| 99 | |⌃ + F|光标向右移动| 100 | |⌃ + A , ⌘ + ←|光标移动到行最左| 101 | |⌃ + E , ⌘ + →|光标移动到行最右| 102 | |⌃ + M|光标在括号里闭合跳转| 103 | |⌘ + ⌃ + ↑|向上移动选中行| 104 | |⌘ + ⌃ + ↓|向下移动选中行| 105 | 106 | 107 | ### 选取 108 | 109 | |按键|命令| 110 | |--|---| 111 | |⌘ + L|选取行| 112 | |⌘ + D|选取一个相同的文本| 113 | |⌃ + ⇧ + G|选取所有相同的文本| 114 | |⌘ + A|选取所有内容| 115 | |⌘ + ⇧ + ↑|向上选取所有内容| 116 | |⌘ + ⇧ + ↓|向下选取所有内容| 117 | |⌘ + ⇧ + K|选取当前行HTML标签| 118 | |⌘ + ⇧ + A|选取HTML标签闭合内容| 119 | |⌘ + ⇧ + J|选取块闭合内容(可以折叠的内容为块)| 120 | |⌃ + ⇧ + M|选取括号内容| 121 | |⌃ + ⇧ + A|选取光标之前内容| 122 | |⌃ + ⇧ + E|选取光标之后内容| 123 | 124 | ### 窗口 125 | 126 | |按键|命令| 127 | |--|---| 128 | |⌘ + K + ⌘ + B|显示左侧栏| 129 | |⌃ + `|显示控制台| 130 | |⌘ + B|执行编译| 131 | |⌃ + ⌘ + F |全屏模式| 132 | |⌃ + ⇧ + ⌘ + F|无干扰全屏模式| 133 | |⌃ + ⇧ + 2 |进行左右分屏| 134 | |⌃ + ⇧ + 5 |进行上下分屏| 135 | |⌃ + ⇧ + 1 |取消所有分屏| 136 | |⌃ + Num |根据数字跳转至对应分屏| 137 | 138 | 139 | 140 | 141 | ## 插件 142 | 143 | ### PackageControl 插件 144 | 145 | 按下 `⌃ + ~` 打开控制台,复制粘贴以下代码并按下 `↩`: 146 | ``` 147 | import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read()) 148 | ``` 149 | 150 | ### Material Theme 插件 151 | 152 | `Material Theme` 是一款编辑器主题插件 153 | 154 | 可以使用 `Package Control` 插件安装 `Material-Theme` 插件 155 | 156 | `Material-Theme` 插件可提供三种外观样式: 157 | ``` 158 | //default 159 | "theme": "Material-Theme.sublime-theme", 160 | "color_scheme": "Packages/Material Theme/schemes/Material-Theme.tmTheme", 161 | 162 | //Darker 163 | "theme": "Material-Theme-Darker.sublime-theme", 164 | "color_scheme": "Packages/Material Theme/schemes/Material-Theme-Darker.tmTheme", 165 | 166 | //Lighter 167 | "theme": "Material-Theme-Lighter.sublime-theme", 168 | "color_scheme": "Packages/Material Theme/schemes/Material-Theme-Lighter.tmTheme", 169 | ``` 170 | 171 | ### AdvanceNewFile 插件 172 | 173 | `AdvanceNewFile` 是一款快速操作文件的插件 174 | 175 | 可以使用 `Package Control` 安装 `AdvanceNewFile` 插件 176 | 177 | 安装完毕之后,可以按下 `⌘ + ⇧ + P` 调出命令面板,执行以下命令对文件进行操作 178 | 179 | ``` 180 | //新增 181 | ANF:New File 182 | 183 | //删除 184 | ANF:Delete File 185 | 186 | //重命名 187 | ANF:Rename File 188 | 189 | //复制当前文件 190 | ANF:Copy Current File 191 | 192 | //删除当前文件 193 | ANF:Delete Current File 194 | ``` 195 | 196 | 亦可使用快捷键 `⌘ + ⌥ + N` 快速新建文件 197 | 198 | 199 | ### PHP Getters and Setters 插件 200 | 201 | `PHP Getters and Setters` 是一款根据类文件的成员属性,快速生成 Getter 方法和 Setter 方法的插件 202 | 203 | 可以使用 `Package Control` 安装 `PHP Getters and Setters` 插件 204 | 205 | 执行以下命令快速生成 Getter 和 Setter 的代码 206 | ``` 207 | // 生成 Getter 方法 208 | PHP:Generate Getters 209 | // 生成 Setter 方法 210 | PHP:Generate Setters 211 | 212 | // 同时生成 Getter 和 Setter 213 | PHP:Generate Getters and Setters 214 | ``` 215 | 216 | ### PHP Companion 插件 217 | 218 | `PHP Companion` 插件提供以下功能 219 | 220 | - `find_use` 221 | 生成所选类的use声明 222 | 223 | - `expand_fqcn` 224 | 生成所选类的引用路径 225 | 226 | - `import_namespace` 227 | 生成所选文件的命名空间 228 | 229 | - `goto_definition_scope` 230 | 寻找所选代码的定义源头 231 | 232 | - `insert_php_constructor_property` 233 | 生成当前类的构造方法 234 | 235 | 在 `Preferences` -> `Key Bindings - User` 新增以下快捷键调用上述插件命令 236 | ```json 237 | //PHP Companion 238 | {"keys":["f9"],"command":"find_use"}, 239 | {"keys":["f10"],"command":"expand_fqcn"}, 240 | {"keys":["f11"],"command":"goto_definition_scope"}, 241 | {"keys":["f12"],"command":"insert_php_constructor_property"}, 242 | ``` 243 | 244 | ### Laravel Artisan 插件 245 | 246 | `Laravel Artisan` 是一款让开发可以不使用终端就能运行 `Artisan CLI` 的插件 247 | 248 | 命令与在终端使用 `php artisan` 基本一致,可以按下 `⌘ + ⇧ + P` 打开命令面板,执行以下命令: 249 | 250 | ```json 251 | // 生成控制器 252 | Laravel Artisan5:Make:Controller 253 | 254 | // 生成请求 255 | Laravel Artisan5:Make:Request 256 | 257 | // 生成服务提供者 258 | Laravel Artisan5:Make:Provider 259 | 260 | // 生成数据迁移文件 261 | Laravel Artisan5:Make:Mrgiation 262 | ``` 263 | 264 | ### DocBlockr 插件 265 | 266 | `DocBlockr` 是一款根据代码自动生成注释的插件 267 | 268 | ### All Autocomplete 插件 269 | 270 | `All Autocomplete` 是一款自动补全的插件,它会查找你打开的所有文件的代码,然后进行代码补全 271 | 272 | 273 | ### SublimeLinter-php 插件 274 | 275 | `SublimeLinter` 是一款检查PHP语言错误的插件 276 | 277 | 使用 `Package Control` 安装 `SublimeLinter` 插件 278 | 使用 `Package Control` 安装 `SublimeLinter-php` 插件 279 | 280 | 在 `Preferences` -> `Package Settings` -> `SublimeLinter` -> `Settings - User` 新增以下代码: 281 | 282 | ``` 283 | "show_errors_on_save": true, 284 | ``` 285 | 上述操作会让编辑器在保存文件的时候,提示错误信息 286 | 287 | 288 | 289 | ## 代码片段 290 | 291 | `Sublime Text` 可以创建自定义的代码片段 292 | 293 | 点击菜单栏 `Tools` -> `New Snippet` ,创建文件 `PHP Public Method.sublime-snippet` 294 | ``` 295 | <snippet> 296 | <content><![CDATA[ 297 | public function ${1:function_name}(${2:param}){ 298 | ${3:return result} 299 | } 300 | ]]></content> 301 | <!-- Optional: Set a tabTrigger to define how to trigger the snippet --> 302 | <tabTrigger>pubf</tabTrigger> 303 | <!-- Optional: Set a scope to limit where the snippet will trigger --> 304 | <scope>source.php</scope> 305 | </snippet> 306 | ``` 307 | 当在PHP文件写代码时,输入 `pubf` 并按下 `Tab` 键,会生成相对应的代码片段 308 | ``` 309 | public function function_name(param){ 310 | return true 311 | } 312 | ``` 313 | 314 | 315 | ## 格式化代码 316 | 317 | 在终端执行以下命令安装 `php-cs-fix` 318 | ```shell 319 | brew install php-cs-fixer 320 | ``` 321 | 322 | 假如要对某个 PHP 文件进行格式化操作,运行以下命令: 323 | ``` 324 | cd dir 325 | php-cs-fixer fix fileName.php --level="psr2" 326 | ``` 327 | 328 | 使用 `Sublime Text 3` 的 `Build System` 格式化代码 329 | 点击 `Tools` -> `Build System` -> `New Bulid System ...`,打开代码如下: 330 | ``` 331 | "shell_cmd":"make" 332 | ``` 333 | 修改上述代码如下: 334 | ``` 335 | { 336 | "shell_cmd": "php-cs-fixer fix $file --level=psr2" 337 | } 338 | ``` 339 | 保存之后,在 `Tools`-`Build System` 选择 `PSR-2`,按 `⌘ + B `对当前文件进行 `PSR-2` 格式化操作。 340 | 341 | 觉得格式化提示信息麻烦,可以在`Preferences`-`Settings - User`添加 342 | ``` 343 | "show_panel_on_build":false 344 | ``` 345 | 346 | 347 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /博客文章/使用Laravel构建内容管理框架(一).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 构建内容管理框架(一) 3 | date: 2016-03-09 20:54:07 4 | tags: Laravel 5 | category: PHP 6 | --- 7 | 8 | 为使用 Laravel 构建内容管理框架做好环境、代码和脚本的准备 9 | 10 | # 安装Laravel 11 | 12 | *** 13 | 14 | 在终端使用Composer安装Laravel 15 | ``` 16 | sudo composer create-project laravel/laravel backend --prefer-dist 5.1.x 17 | ``` 18 | 执行后,会在当前目录下新建一个文件夹,里面就是新建的Laravel项目 19 | 20 | # 安装gulp、bower 21 | 22 | *** 23 | 24 | 在终端使用npm安装gulp 25 | ``` 26 | sudo npm install -g gulp #全局安装npm 27 | sudo npm install -g bower #全局安装bower 28 | 29 | cd backend 30 | sudo npm install gulp #在项目本地安装gulp 31 | sudo npm install bower #在项目本地安装bower 32 | sudp npm install #安装项目Node依赖、Laravel Elixir 33 | 34 | ``` 35 | 36 | 运行`gulp tdd`之后,会自动监控测试单元 37 | 38 | 39 | 40 | # 使用bower集成前端依赖库 41 | 42 | *** 43 | 44 | 在项目根目录新增文件`.bowerrc` 45 | ``` 46 | { 47 | "directory": "vendor/bower" 48 | } 49 | 50 | ``` 51 | 52 | 上述配置告诉bower在`vendor/bower`存放下载文件 53 | 执行命令`bower init`创建文件`bower.json` 54 | 55 | 56 | 依赖安装所需前端资源 57 | ``` 58 | sudo bower install admin-lte 59 | sudo bower install fontawesome 60 | sudo bower install ionicons 61 | ``` 62 | 63 | 打开文件`gulpfile.js`,编辑如下: 64 | 65 | ``` 66 | var gulp = require('gulp'); 67 | var elixir = require('laravel-elixir'); 68 | 69 | gulp.task('copy', function () { 70 | // jQuery 71 | gulp.src("vendor/bower/AdminLTE/plugins/jQuery/jQuery-2.1.4.min.js") 72 | .pipe(gulp.dest("resources/assets/js/")); 73 | 74 | // bootstarp 75 | gulp.src("vendor/bower/AdminLTE/bootstrap/css/bootstrap.min.css") 76 | .pipe(gulp.dest("resources/assets/css/")); 77 | gulp.src("vendor/bower/AdminLTE/bootstrap/js/bootstrap.min.js") 78 | .pipe(gulp.dest("resources/assets/js/")); 79 | 80 | // AdminLTE 81 | gulp.src("vendor/bower/AdminLTE/dist/css/AdminLTE.min.css") 82 | .pipe(gulp.dest("resources/assets/css/")); 83 | gulp.src("vendor/bower/AdminLTE/dist/css/skins/skin-blue.min.css") 84 | .pipe(gulp.dest("resources/assets/css/")); 85 | gulp.src("vendor/bower/AdminLTE/dist/js/app.min.js") 86 | .pipe(gulp.dest("resources/assets/js/")); 87 | gulp.src("vendor/bower/AdminLTE/dist/img/*") 88 | .pipe(gulp.dest("public/assets/img/")); 89 | 90 | // Fontawesome 91 | gulp.src("vendor/bower/font-awesome/css/font-awesome.min.css") 92 | .pipe(gulp.dest("resources/assets/css/")); 93 | gulp.src("vendor/bower/font-awesome/fonts/*") 94 | .pipe(gulp.dest("public/assets/fonts/")); 95 | 96 | // Ionicons 97 | gulp.src("vendor/bower/Ionicons/css/ionicons.min.css") 98 | .pipe(gulp.dest("resources/assets/css/")); 99 | gulp.src("vendor/bower/Ionicons/fonts/*") 100 | .pipe(gulp.dest("public/assets/fonts/")); 101 | 102 | // slimScroll 103 | gulp.src("vendor/bower/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js") 104 | .pipe(gulp.dest("resources/assets/js/")); 105 | 106 | // iCheck 107 | gulp.src("vendor/bower/AdminLTE/plugins/iCheck/icheck.min.js") 108 | .pipe(gulp.dest("resources/assets/js/")); 109 | gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue.css") 110 | .pipe(gulp.dest("resources/assets/css/")); 111 | gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue.png") 112 | .pipe(gulp.dest("public/assets/css/")); 113 | gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue@2x.png") 114 | .pipe(gulp.dest("public/assets/css/")); 115 | 116 | // select2 117 | gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.full.min.js") 118 | .pipe(gulp.dest("resources/assets/js/")); 119 | gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.min.js") 120 | .pipe(gulp.dest("resources/assets/js/")); 121 | gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.min.css") 122 | .pipe(gulp.dest("resources/assets/css/")); 123 | 124 | // pace 125 | gulp.src("vendor/bower/AdminLTE/plugins/pace/pace.min.css") 126 | .pipe(gulp.dest("resources/assets/css/")); 127 | gulp.src("vendor/bower/AdminLTE/plugins/pace/pace.min.js") 128 | .pipe(gulp.dest("resources/assets/js/")); 129 | }); 130 | 131 | elixir(function (mix) { 132 | // 合并javascript脚本 133 | mix.scripts( 134 | [ 135 | 'jQuery-2.1.4.min.js', 136 | 'bootstrap.min.js', 137 | 'app.min.js', 138 | 'pace.min.js', 139 | 'jquery.slimscroll.min.js', 140 | 'icheck.min.js', 141 | 'select2.full.min.js', 142 | 'select2.min.js' 143 | ], 144 | 'public/assets/js/app.js', 145 | 'resources/assets/js/' 146 | ); 147 | 148 | // 合并css脚本 149 | mix.styles( 150 | [ 151 | 'bootstrap.min.css', 152 | 'pace.min.css', 153 | 'select2.min.css', 154 | 'AdminLTE.min.css', 155 | 'skin-blue.min.css', 156 | 'font-awesome.min.css', 157 | 'ionicons.min.css', 158 | 'blue.css' 159 | ], 160 | 'public/assets/css/app.css', 161 | 'resources/assets/css/' 162 | ); 163 | 164 | // 运行单元测试 165 | mix.phpUnit(); 166 | }); 167 | 168 | ``` 169 | 170 | 171 | 在终端执行命令如下: 172 | ``` 173 | gulp copy 174 | gulp 175 | ``` 176 | 177 | 上述操作将会在根目录文件夹`public/assets/`生成所需的前端资源,接下来就是在视图模板中使用。 178 | -------------------------------------------------------------------------------- /博客文章/使用Laravel构建内容管理框架(三).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 构建内容管理框架(三) 3 | date: 2016-04-04 20:33:12 4 | tags: Laravel 5 | category: PHP 6 | --- 7 | 8 | 完成登录管理页面与登录验证功能 9 | 10 | # 管理用户模型 11 | 12 | *** 13 | 14 | 在文件夹`app`下新建文件夹`Models`,将文件夹`app`下的`User.php`移动到文件夹`app/Models`下 15 | 修改文件`User.php`的命名空间 16 | ``` 17 | namespace App\Models; 18 | ``` 19 | 20 | 打开文件`config/auth.php`,修改代码如下 21 | ``` 22 | 'model' => App\Models\User::class, 23 | ``` 24 | 25 | # 新增路由 26 | 27 | *** 28 | 打开文件`app/Http/routes.php`,文件代码如下: 29 | ``` 30 | <?php 31 | Route::group(['namespace' => 'Backend', 'middleware' => ['auth']], function () { 32 | Route::get('/', 'IndexController@index'); 33 | }); 34 | 35 | Route::group(['namespace' => 'Auth'], function () { 36 | Route::get('auth/login', 'AuthController@getLogin'); 37 | Route::post('auth/login', 'AuthController@postLogin'); 38 | Route::get('auth/logout', 'AuthController@getLogout'); 39 | }); 40 | ``` 41 | 42 | # 新增登录视图 43 | 44 | *** 45 | 46 | 在文件夹`resources/views/`新建文件夹`auth`,然后新建视图文件`login.blade.php` 47 | 在文件夹`resources/views/backend/layout`新建文件夹`message`,然后新建视图文件如下: 48 | 49 | - tips.blade.php 50 | - error.blade.php 51 | - success.blade.php 52 | 53 | ## login.blade.php 54 | ``` 55 | <!DOCTYPE html> 56 | <html lang="zh"> 57 | <head> 58 | <meta charset="utf-8"> 59 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> 60 | <title>AdminLTE 2 | Log in 61 | 62 | 63 | 64 | 65 |
66 | 69 | 106 |
107 | 108 | 117 | 118 | 119 | ``` 120 | 121 | ## tips.blade.php 122 | ``` 123 | @if($errors->has($field)) 124 | @foreach ($errors->get($field) as $error) 125 | 128 | @endforeach 129 | @endif 130 | ``` 131 | 132 | ## error.blade.php 133 | ``` 134 | @if(Session::has('errors')) 135 |
136 | 137 |

错误提示!

138 | @foreach($errors->all() as $error) 139 | {{$error}} 140 | @endforeach 141 |
142 | @endif 143 | ``` 144 | 145 | ## success.blade.php 146 | ``` 147 | @if(Session::has('success')) 148 |
149 | 150 |

成功提示!

151 | {{Session::get('success')}} 152 |
153 | @endif 154 | ``` 155 | 156 | # 新增验证码Package 157 | 158 | *** 159 | 160 | 打开`composer.json`文件,修改代码如下: 161 | ``` 162 | "require": { 163 | "php": ">=5.5.9", 164 | "laravel/framework": "5.1.*", 165 | "gregwar/captcha": "1.*" 166 | }, 167 | ``` 168 | 169 | `gregwar/captcha`是验证码Package,用来生成验证码,在终端执行以下命令进行安装。 170 | ``` 171 | sudo composer install 172 | ``` 173 | 174 | # 管理登录认证控制器 175 | 176 | 打开文件`app/Http/Auth/AuthController.php`,新增代码如下: 177 | ``` 178 | use Illuminate\Support\Facades\Session; 179 | 180 | public function getLogin() 181 | { 182 | if (view()->exists('auth.authenticate')) { 183 | return view('auth.authenticate'); 184 | } 185 | 186 | $builder = new CaptchaBuilder(); 187 | $builder->build(); 188 | Session::put('phrase', $builder->getPhrase()); 189 | 190 | return view('auth.login')->with('captcha', $builder->inline()); 191 | } 192 | 193 | public function postLogin(Request $request) 194 | { 195 | $this->validate($request, [ 196 | $this->loginUsername() => 'required', 197 | 'password' => 'required', 198 | 'captcha' => 'required' 199 | ], [ 200 | $this->loginUsername() . '.required' => '请输入邮箱或用户名称', 201 | 'password.required' => '请输入用户密码', 202 | 'captcha.required' => '请输入验证码' 203 | ]); 204 | 205 | if (Session::get('phrase') !== $request->get('captcha')) { 206 | return redirect()->back()->withErrors(array('captcha' => '验证码不正确'))->withInput(); 207 | } 208 | } 209 | ``` 210 | 211 | # 使用迁移文件生成数据表 212 | 213 | *** 214 | 215 | 接下来打开终端,在项目下运行以下命令,生成用户表并创建用户 216 | ``` 217 | // 生成数据表 218 | php artisan migrate 219 | 220 | // 生成用户数据 221 | php artisan tinker 222 | $user = new App\Models\User; 223 | $user->name = "admin"; 224 | $user->email = "admin@qq.com"; 225 | $user->password = bcrypt(123456); 226 | $user->save(); 227 | exit 228 | ``` 229 | 230 | # 管理登录认证操作跳转页面 231 | 232 | 打开文件`app\Http\Controller\Auth\AuthController`,新增以下成员属性: 233 | ``` 234 | // 设置成功登录后转向的页面: 235 | protected $redirectPath = '/'; 236 | 237 | // 设置登录失败后转向的页面: 238 | protected $loginPath = '/auth/login'; 239 | 240 | // 设置退出登录后转向的页面: 241 | protected $redirectAfterLogout = '/'; 242 | ``` 243 | 打开中间件`app\Http\Middleware\RedirectIfAuthenticated.php`,找到相应的地方,修改代码如下: 244 | 245 | ``` 246 | public function handle($request, Closure $next) 247 | { 248 | // 设置登录之后,输入/auth/login的跳转地址 249 | if ($this->auth->check()) { 250 | return redirect('/'); 251 | } 252 | 253 | return $next($request); 254 | } 255 | ``` 256 | -------------------------------------------------------------------------------- /博客文章/使用Laravel构建内容管理框架(六).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 Laravel 构建内容管理框架(六) 3 | date: 2016-04-04 21:33:28 4 | tags: Laravel 5 | category: PHP 6 | --- 7 | 8 | 完成Entrust的配置 9 | 10 | # 安装zizaco/entrust Package 11 | 12 | *** 13 | 14 | zizaco/entrust是Laravel下基于角色的权限管理方案。 15 | 16 | 1.修改文件`composer.json` 17 | ``` 18 | "require": { 19 | "php": ">=5.5.9", 20 | "laravel/framework": "5.1.*", 21 | "gregwar/captcha": "1.*", 22 | "zizaco/entrust": "dev-laravel-5" 23 | }, 24 | ``` 25 | 26 | 2.在终端运行命令 27 | ``` 28 | composer install 29 | 30 | // or 31 | 32 | composer update 33 | ``` 34 | 35 | 3.在文件`config/app.php`的`providers`数组和`aliase`数组分别添加 36 | ``` 37 | // providers 38 | Zizaco\Entrust\EntrustServiceProvider::class 39 | 40 | // aliase 41 | 'Entrust' => Zizaco\Entrust\EntrustFacade::class, 42 | ``` 43 | 44 | 45 | 4.在文件`app/Http/Kernel.php`的`$routeMiddleware`添加 46 | ``` 47 | 'role' => Zizaco\Entrust\Middleware\EntrustRole::class, 48 | 'permission' => Zizaco\Entrust\Middleware\EntrustPermission::class, 49 | 'ability' => Zizaco\Entrust\Middleware\EntrustAbility::class, 50 | ``` 51 | 52 | 5.Entrust会根据文件`config/auth.php`的`model`来选择用户模型,如下: 53 | ``` 54 | 'model' => App\Models\User::class 55 | ``` 56 | 57 | 执行以下命令生成配置文件`config/entrust.php` 58 | ``` 59 | php artisan vendor:publish 60 | ``` 61 | 修改配置文件`entrust.php`如下: 62 | ``` 63 | 'role'=>'App\Models\Role' 64 | 'permission'=>'App\Models\Permission' 65 | ``` 66 | 67 | 6.生成数据库迁移文件并执行 68 | ``` 69 | php artisan entrust:migration 70 | php artisan migrate 71 | ``` 72 | 73 | 7.生成自动加载 74 | ``` 75 | composer dump-autoload 76 | ``` 77 | 78 | # 管理模型 79 | 80 | *** 81 | 82 | 为了使用Entrust,我们需要新建两个模型,在终端运行以下命令: 83 | ``` 84 | // 角色模型 85 | php artisan make:model Models/Role 86 | 87 | // 权限模型 88 | php artisan make:model Models/Permission 89 | ``` 90 | 91 | ## Role.php 92 | ``` 93 | = 4.0.6, PHP 5, PHP 7) 11 | array_filter — 用回调函数过滤数组中的单元 12 | 13 | ## 说明 14 | 15 | `array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )` 16 | 17 | 依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 TRUE,则 input 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。 18 | 19 | ## 参数 20 | 21 | - array 22 | - 要循环的数组 23 | - callback 24 | - 使用的回调函数 25 | - 如果没有提供 callback 函数, 将删除 input 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。 26 | - flag 27 | - 决定callback接收的参数形式: 28 | - ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数 29 | - ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值 30 | 31 | ## 返回值 32 | 33 | 返回过滤后的数组。 34 | 35 | 36 | ## 范例 37 | ```php 38 | '华硕', 43 | 'model'=>'GTX980(Ti)', 44 | 'status'=>1, 45 | ], 46 | [ 47 | 'brand'=>'技嘉', 48 | 'model'=>'GTX970', 49 | 'status'=>0, 50 | ], 51 | [ 52 | 'brand'=>'微星', 53 | 'model'=>'GTX960', 54 | 'status'=>1, 55 | ], 56 | [ 57 | 'brand'=>'七彩虹', 58 | 'model'=>'GTX950', 59 | 'status'=>0, 60 | ], 61 | ]; 62 | 63 | $data = [ 64 | 'int'=>0, 65 | 'null'=>null, 66 | 'bool'=>false, 67 | 'string'=>'hello', 68 | 'number'=>506510463, 69 | 'int-string'=>'0', 70 | 'null-string'=>'', 71 | ]; 72 | var_dump(array_filter($data)); 73 | 74 | var_dump(array_filter($data, function ($value) { if (!empty($value)) { 75 | return $value; 76 | }})); 77 | 78 | var_dump(array_filter($data, 'strlen')); 79 | 80 | var_dump(array_map('intval', $data)); 81 | 82 | var_dump(array_map(function ($value) { if (!empty($value)) { 83 | return $value; 84 | }}, $data)); 85 | 86 | var_dump(array_filter(array_map(function ($value) { if ($value['status']===1) { 87 | return $value['brand'].$value['model']; 88 | }}, $graphics_card))); 89 | ``` 90 | -------------------------------------------------------------------------------- /博客文章/关于-PHP-函数-array-map-的一些想法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于 PHP 函数 array_map 的一些想法 3 | date: 2016-11-21 14:42:50 4 | tags: PHP 5 | category: PHP 6 | --- 7 | 8 | 9 | ## array_map 10 | 11 | array_map - 为数组的每个元素应用回调函数 12 | 13 | ## 说明 14 | 15 | `array array_map(callable $callback, array $array1[, array $...])` 16 | 17 | array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。 18 | 19 | ## 参数 20 | 21 | - callback 22 | - 回调函数,应用到每个数组里的元素 23 | - array1 24 | - 数组,遍历运行 callback 函数 25 | - ... 26 | - 数据列表,每个都遍历运行 callback 函数 27 | 28 | ## 范例 29 | 30 | ### array_map 实现 foreach 的效果 31 | ```php 32 | brand = $brand; 42 | $this->model = $model; 43 | } 44 | } 45 | 46 | $graphics_card = [ 47 | [ 48 | 'brand'=>'华硕', 49 | 'model'=>'GTX980(Ti)', 50 | 'status'=>1, 51 | ], 52 | [ 53 | 'brand'=>'技嘉', 54 | 'model'=>'GTX970', 55 | 'status'=>0, 56 | ], 57 | [ 58 | 'brand'=>'微星', 59 | 'model'=>'GTX960', 60 | 'status'=>1, 61 | ], 62 | [ 63 | 'brand'=>'七彩虹', 64 | 'model'=>'GTX950', 65 | 'status'=>0, 66 | ], 67 | ]; 68 | 69 | 70 | $graphics_card2 = []; 71 | foreach ($graphics_card as $value) { 72 | $graphics_card2[] = new GraphicsCard($value['brand'], $value['model']); 73 | } 74 | 75 | // Demo 1 76 | $demoA1 = $graphics_card; 77 | $demoA2 = $graphics_card2; 78 | foreach ($demoA1 as $key => $value) { 79 | $demoA1[$key]['model'] = '1080'; 80 | $demoA1[$key]['brand'] = '按摩店'; 81 | } 82 | foreach ($demoA2 as $key => $item) { 83 | $demoA2[$key]->brand = '按摩店'; 84 | $demoA2[$key]->model = '1080'; 85 | } 86 | var_dump($demoA1); 87 | var_dump($demoA2); 88 | 89 | // Demo 2 90 | $demoB1 = $graphics_card; 91 | $demoB2 = $graphics_card2; 92 | $demoB1 = array_map(function ($value) { 93 | $value['brand'] = '按摩店'; 94 | $value['model'] = '1080'; 95 | return $value; 96 | }, $demoB1); 97 | array_map(function ($value) { 98 | $value->brand = '按摩店'; 99 | $value->model = '1080'; 100 | }, $demoB2); 101 | var_dump($demoB1); 102 | var_dump($demoB2); 103 | 104 | // Demo 3 105 | $demoC1 = $graphics_card; 106 | $demoC2 = $graphics_card2; 107 | array_map(function ($key, $value) use (&$demoC1) { 108 | $demoC1[$key]['brand'] = '按摩店'; 109 | $demoC1[$key]['model'] = '1080'; 110 | }, array_keys($demoC1), $demoC1); 111 | array_map(function ($key, $value) use ($demoC2) { 112 | $demoC2[$key]->brand = '按摩店'; 113 | $demoC2[$key]->model = '1080'; 114 | }, array_keys($demoC2), $demoC2); 115 | var_dump($demoC1); 116 | var_dump($demoC2); 117 | 118 | // Demo 4 119 | $brands = []; 120 | $models = []; 121 | foreach ($graphics_card as $item) { 122 | $brands[] = $item['brand']; 123 | } 124 | 125 | $models = array_map(function ($value) { 126 | return $value['model']; 127 | }, $graphics_card); 128 | 129 | var_dump($brands); 130 | var_dump($models); 131 | // 也可以使用 array_column 来实现 Demo 4 的效果 132 | var_dump(array_column($graphics_card, 'brand')); 133 | var_dump(array_column($graphics_card, 'model')); 134 | 135 | // Demo5 136 | $items = [ 137 | 'item1'=>'Hello', 138 | 'item2'=>'World', 139 | 'item3'=>'Luis', 140 | 'item4'=>'Edware', 141 | 'item5'=>'Ann', 142 | 'item6'=>'Eason', 143 | ]; 144 | 145 | foreach($items as $key => $value){ 146 | if($key === 'item1'){ 147 | $items[$key] = 'World'; 148 | }elseif($key === 'item3'){ 149 | $items[$key] = 'Edware'; 150 | } 151 | } 152 | var_dump($items); 153 | 154 | $items_to_modify = ['item1'=>'Hello','item3'=>'Luis']; 155 | array_map(function($key,$value) use(&$items) { 156 | $items[$key] = $value; 157 | },array_keys($items_to_modify),$items_to_modify); 158 | var_dump($items); 159 | ``` 160 | 161 | ## 使用 array_map 实现 array_column 的效果 162 | ```php 163 | '华硕', 46 | 'model'=>'GTX980(Ti)', 47 | 'status'=>1, 48 | ], 49 | [ 50 | 'brand'=>'技嘉', 51 | 'model'=>'GTX970', 52 | 'status'=>0, 53 | ], 54 | [ 55 | 'brand'=>'微星', 56 | 'model'=>'GTX960', 57 | 'status'=>1, 58 | ], 59 | [ 60 | 'brand'=>'七彩虹', 61 | 'model'=>'GTX950', 62 | 'status'=>0, 63 | ], 64 | ]; 65 | 66 | array_walk($graphics_card, function(&$item){ 67 | $item['brand'] = '按摩店'; 68 | $item['model'] = '1080'; 69 | }); 70 | var_dump($graphics_card); 71 | 72 | array_map(function($key) use (&$graphics_card) { 73 | $graphics_card[$key]['brand'] = '英伟达'; 74 | $graphics_card[$key]['model'] = '泰坦'; 75 | }, array_keys($graphics_card)); 76 | var_dump($graphics_card); 77 | 78 | ``` 79 | -------------------------------------------------------------------------------- /博客文章/复习使用-MySQL(一).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 复习使用 MySQL(一) 3 | date: 2016-09-02 20:59:45 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | ## 环境 9 | 10 | - MySQL 5.7.13 11 | 12 | ## MySQL 及衍生版本 13 | 14 | - MySQL 15 | - MariaDB 16 | - Percona Server 17 | 18 | 19 | ## MySQL SQL 基础 20 | 21 | - ### DDL(Data Definition Language) - 数据定义 22 | - TRUNCATE - 清空 23 | - TRUNCATE TABLE Syntax - 清空数据表 24 | - RENAME - 重命名 25 | - RENAME TABLE Syntax - 重命名数据表 26 | - CREATE - 创建 27 | - CREATE VIEW Syntax - 创建视图 28 | - CREATE EVENT Syntax - 创建事件 29 | - CREATE INDEX Syntax - 创建索引 30 | - CREATE TABLE Syntax - 创建数据表 31 | - CREATE SERVER Syntax - 创建服务器 32 | - CREATE TRIGGER Syntax - 创建触发器 33 | - CREATE FUNCTION Syntax - 创建自定义函数 34 | - CREATE DATABASE Syntax - 创建数据库 35 | - CREATE PROCEDURE Syntax - 创建存储过程 36 | - CREATE TABLESPACE Syntax - 创建数据表空间 37 | - CREATE LOGFILE GROUP Syntax - 创建日志文件组 38 | - ALTER - 修改 39 | - ALTER VIEW Syntax - 修改视图 40 | - ALTER EVENT Syntax - 修改事件 41 | - ALTER TABLE Syntax - 修改数据表 42 | - ALTER SERVER Syntax - 修改服务器 43 | - ALTER DATABASE Syntax - 修改数据库 44 | - ALTER FUNCTION Syntax - 修改自定义函数 45 | - ALTER INSTANCE Syntax - 修改实例 46 | - ALTER PROCEDURE Syntax - 修改存储过程 47 | - ALTER TABLESPACE Syntax - 修改数据表空间 48 | - ALTER LOGFILE GROUP Syntax - 修改日志分组 49 | - DROP - 删除 50 | - DROP VIEW Syntax - 删除视图 51 | - DROP EVENT Syntax - 删除事件 52 | - DROP INDEX Syntax - 删除索引 53 | - DROP TABLE Syntax - 删除数据表 54 | - DROP SERVER Syntax - 删除服务器 55 | - DROP TRIGGER Syntax - 删除触发器 56 | - DROP DATABASE Syntax - 删除数据库 57 | - DROP FUNCTION Syntax - 删除自定义函数 58 | - DROP PROCEDURE Syntax - 删除存储过程 59 | - DROP TABLESPACE Syntax - 删除数据表空间 60 | - DROP LOGFILE GROUP Syntax - 删除日志分组 61 | - ### DML(Data Manipulation Language) - 数据操作 62 | - DO 63 | - CALL 64 | - HANDLER 65 | - LOAD XML 66 | - LOAD DATA INFILE 67 | - INSERT - 数据插入 68 | - DELETE - 数据删除 69 | - UPDATE - 数据修改 70 | - SELECT - 数据查询 71 | - JOIN - 连接 72 | - LEFT JOIN - 左连接 73 | - RIGHT JOIN - 右连接 74 | - INNER JOIN - 内连接 75 | - GROUP - 分组 76 | - UNION - 联合 77 | - REPLACE 78 | - Subquery - 子查询 79 | - LIMIT, OFFSET - 分页 80 | - ### DCL(Data Control Language) - 数据控制 81 | - GRANT - 授权 82 | - REVOKE - 撤销授权 83 | - ### Transactions and Lock - 事务 84 | - START TRANSACTION, COMMIT, and ROLLBACK Syntax - 事务开始,事务提交和事务回滚 85 | - Statements That Cannot Be Rolled Back - 不能执行回滚的语句 86 | - Statements That Cause an Implicit Commit - 隐式提交 87 | - SAVEPOINT, ROLLBACK TO SAVEPOINT, and RELEASE SAVEPOINT Syntax - 保存点,回滚到保存点,释放保存点 88 | - LOCK TABLES and UNLOCK TABLES Syntax - 锁表与解锁 89 | - SET TRANSACTION Syntax - 设置事务语法 90 | - XA Transactions - XA 事务 91 | - ### MySQL Utility Statements - MySQL 实用语句 92 | - USE Syntax - 设置当前 SQL 语句默认使用数据库 93 | - HELP Syntax - 从 MySQL 参考手册返回在线信息 94 | - EXPLAIN Syntax - 获取 SQL 执行信息 95 | - DESCRIBE Syntax - 获取表结构信息 96 | - ### Database Administration Statements - 数据库管理员语句 97 | - SET Syntax - 设置 98 | - SET NAMES Syntax 99 | - SET CHARACTER SET Syntax 100 | - SET Syntax for Variable Assignment 101 | - SHOW Syntax - 查看 102 | - SHOW BINARY LOGS Syntax - 显示二进制日志 103 | - SHOW BINLOG EVENTS Syntax - 显示二进制事件 104 | - SHOW CHARACTER SET Syntax - 显示字符集 105 | - SHOW COLLATION Syntax - 显示支持的字符 106 | - SHOW COLUMNS Syntax - 显示字段信息 107 | - SHOW CREATE DATABASE Syntax - 显示创建指定数据库的 SQL 语句 108 | - SHOW CREATE EVENT Syntax - 显示创建指定事件的 SQL 语句 109 | - SHOW CREATE FUNCTION Syntax - 显示创建指定自定义函数的 SQL 语句 110 | - SHOW CREATE PROCEDURE Syntax - 显示创建指定存储过程的 SQL 语句 111 | - SHOW CREATE TABLE Syntax - 显示创建指定表的 SQL 语句 112 | - SHOW CREATE TRIGGER Syntax - 显示创建指定触发器的 SQL 语句 113 | - SHOW CREATE USER Syntax - 显示创建指定用户的 SQL 语句 114 | - SHOW CREATE VIEW Syntax - 显示创建指定视图的 SQL 语句 115 | - SHOW DATABASES Syntax - 显示数据库列表 116 | - SHOW ENGINE Syntax - 显示引擎的操作信息 117 | - SHOW ENGINES Syntax - 显示支持的引擎 118 | - SHOW ERRORS Syntax - 显示错误 119 | - SHOW EVENTS Syntax - 显示事件 120 | - SHOW FUNCTION CODE Syntax - 显示自定义函数代码 121 | - SHOW FUNCTION STATUS Syntax - 显示自定义函数状态 122 | - SHOW GRANTS Syntax 123 | - SHOW INDEX Syntax - 显示索引 124 | - SHOW MASTER STATUS Syntax 125 | - SHOW OPEN TABLES Syntax 126 | - SHOW PLUGINS Syntax 127 | - SHOW PRIVILEGES Syntax - 显示当前用户权限 128 | - SHOW PROCEDURE CODE Syntax - 显示存储过程代码 129 | - SHOW PROCEDURE STATUS Syntax - 显示存储过程状态 130 | - SHOW PROCESSLIST Syntax 131 | - SHOW PROFILE Syntax - 显示配置文件 132 | - SHOW PROFILES Syntax - 显示配置文件列表 133 | - SHOW RELAYLOG EVENTS Syntax 134 | - SHOW SLAVE HOSTS Syntax - 查看附属主机 135 | - SHOW SLAVE STATUS Syntax - 查看附属主机状态 136 | - SHOW STATUS Syntax - 查看各种状态 137 | - SHOW TABLE STATUS Syntax - 显示指定数据库的所有表状态 138 | - SHOW TABLES Syntax - 显示指定数据库的所有表 139 | - SHOW TRIGGERS Syntax - 显示触发器 140 | - SHOW VARIABLES Syntax - 显示变量 141 | - SHOW WARNINGS Syntax - 显示警告 142 | - Table Maintenance Statements - 表维护语句 143 | - CHECK TABLE Syntax - 检查表 144 | - REPAIR TABLE Syntax - 修复表 145 | - ANALYZE TABLE Syntax - 分析表 146 | - OPTIMIZE TABLE Syntax - 优化表 147 | - CHECKSUM TABLE Syntax - 校检表 148 | - Account Management Statements - 账号管理语句 149 | - GRANT Syntax - 修复 150 | - REVOKE Syntax - 撤销 151 | - DROP USER Syntax - 删除用户 152 | - ALTER USER Syntax - 修改用户 153 | - CREATE USER Syntax - 创建用户 154 | - RENAME USER Syntax - 重命名用户 155 | - SET PASSWORD Syntax - 设置密码 156 | - Other Administrative Statements - 其他管理语句 157 | - Plugin and User-Defined Function Statements - 插件和用户定义的函数语句 158 | - ### 范式 & 反范式 159 | - 范式 160 | - 第一范式(1NF) 161 | - 第二范式(2NF) 162 | - 第三范式(3NF) 163 | - 巴斯-科德范式(BCNF) 164 | - 第四范式(4NF) 165 | - 第五范式(5NF,又称完美范式) 166 | - 反范式 167 | - 没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。 168 | -------------------------------------------------------------------------------- /博客文章/复习使用-MySQL(三).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 复习使用 MySQL(三) 3 | date: 2016-09-04 21:08:24 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | ## 环境 9 | 10 | - MySQL 5.7.13 11 | 12 | ## MySQL 优化:硬件 13 | 14 | - CPU:使用多核 CPU,能够充分发挥新版 MySQL 多核下的效果,建议 4 核以上 15 | - 内存:如果数据量比较大,建议使用不要低于 20 G 内存的服务器 16 | - 硬盘:SSD 硬盘,提高 TPS,选择适合的 RAID 方案 17 | - 网卡:保证足够的吞吐量,建议千兆网卡 18 | 19 | ## MySQL 优化:软件 20 | 21 | - OS 类型 22 | - Linux 23 | - FreeBSD 24 | - Linux 关键配置 25 | - 文件打开描述符:/etc/security/limits.conf 中 nofile、/proc/sys/fs/nr_open 26 | - 可分配文件句柄数:/etc/sysctl.conf 中 fs.file-max、/proc/sys/fs/file-max 27 | - 进程数:/etc/security/limits.conf 中的 nproc 28 | - 线程数:/proc/sys/kernel/thread-max 29 | - 其他 TCP 和网络相关选项:/etc/sysctl.conf 30 | - 这些都可以通过ulimit 或 直接调整 /proc 中变量进行临时修改 31 | - 建议关闭 SWAP 分区(内存要足够大才行);如果数据太大,为了防止夯死主机,可以设置 2G 左右的 SWAP 分区 32 | 33 | 34 | ## MySQL 优化:基础配置 35 | 36 | 基础配置 & MyISAM 配置 37 | ```c 38 | max_connections=3000 39 | max_user_connections=2800 40 | max_connect_errors=10 41 | max_allowed_packet=64M 42 | max_heap_table_size=512M 43 | tmp_table_size=512M 44 | max_length_for_sort_data=16k 45 | wait_timeout=172800 46 | interactive_timeout=172800 47 | 48 | net_buffer_length=8K 49 | read_buffer_size=4M 50 | read_rnd_buffer_size=1M 51 | sort_buffer_size=256K 52 | join_buffer_size=2M 53 | table_open_cache=512 54 | thread_cache_size=512 55 | query_cache_type=1 56 | query_cache_size=256M 57 | ``` 58 | 59 | InnoDB 配置 60 | ```c 61 | innodb_file_per_table=1 62 | innodb_open_files=7168 63 | innodb_use_sys_malloc=1 64 | innodb_additional_mem_pool_size=64M 65 | innodb_buffer_pool_instances=4 66 | innodb_buffer_pool_size=20G 67 | innodb_data_home_dir=/home/work/data 68 | innodb_data_file_path=ibdata1:1024M:autoextend 69 | innodb_autoextend_increment=128 70 | innodb_thread_concurrency=0 71 | innodb_flush_log_at_trx_commit=1 72 | innodb_fast_shutdown=1 73 | innodb_force_recovery=0 74 | 75 | innodb_log_buffer_size=16M 76 | innodb_log_file_size=128M 77 | innodb_log_files_in_group=4 78 | innodb_log_group_home_dir=/home/work/data 79 | innodb_max_dirty_pages_pct=60 80 | innodb_purge_threads=0 81 | innodb_lock_wait_timeout=50 82 | innodb_rollback_on_timeout=1 83 | innodb_commit_concurrency=0 84 | innodb_concurrency_tickets=1024 85 | innodb_autoinc_lock_mode=2 86 | innodb_change_buffering=all 87 | ``` 88 | 89 | ## MySQL 优化:表设计原则 90 | 91 | - 互联网业务的特点:数据量大、读写操作多、业务模式相对简单 92 | - 单表字段不宜过多,尽量不按照第三范式去设计表结构, 93 | - 尽量减少表关联,适当的冗余保证不联表查询 94 | - 表名和字段尽量采用小写字母+下划线的命名规则,尽量使用英文,例如:user_score 95 | - 每个表一定要有主键,一般建议自增ID;单表索引不宜过多,一般5~6个索引 96 | - 字段尽量使用高效类型,比如数字或时间,IP地址、手机号等都可以使用数字类型存储,非不得已采用字符 97 | 98 | ## MySQL 优化:查询语句 99 | 100 | - 不要写太复杂的SQL语句,尽量简单,能够让业务去做的,就不要让数据库区操作 101 | - 尽量避免:子查询、group by、distinct 等操作(子查询可以用left join替代) 102 | - where、order by 等关键字段一定要建立索引,where里多个条件经常使用的可以建立联合索引(注意建立索引的数据必须是尽量分布多并且具备单调性,重复率低) 103 | - 把数据当做神一样来供着,能够缓存的尽量缓存(redis、memcached等),能够多次查询的就不要关联查询 104 | - 所有的变更操作(update、delete)必须有where条件! 105 | - 不适合使用MySQL服务的数据功能操作,尽量采用别的第三方服务支持,比如全文检索功能 106 | 107 | 108 | 109 | ## MySQL 优化:SQL 优化实例 110 | 111 | - Like 优化 112 | - Limit 优化 113 | - InnoDB 中 count 优化 114 | - 定期使用 Explain 查看执行计划 115 | 116 | 117 | 118 | ## PHP 与 MySQL 交互 119 | 120 | - 扩展层 121 | - 代码层 122 | - 为防止mysql太慢夯住PHP,建议设置SQL超时,或者设置execute_time等超时设置 123 | - $mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3); 124 | - $mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1); 125 | - 务必在代码里记录相关SQL语句执行性能和时间等信息,方便后续优化 126 | 127 | - 超时设置 128 | - PHP代码连接后端的超时设置 129 | - php.ini中execute_time设置 130 | - php-fpm中request_terminate_timeout,request_slowlog_timeout 131 | - nginx upstream相关超时设置 132 | 133 | 134 | ## PHP 与 MySQL 安全 135 | 136 | - SQL 注入 137 | - MySQL 服务端 138 | - 备份机制 139 | -------------------------------------------------------------------------------- /博客文章/复习使用-MySQL(二).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 复习使用 MySQL(二) 3 | date: 2016-09-04 20:41:22 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | ## 环境 9 | 10 | - MySQL 5.7.13 11 | 12 | ## MySQL 主要存储引擎 13 | 14 | 特性/引擎|MyISAM | Aria | InnoDB | XtraDB | Memory | Archive| 15 | ---|---|---|---|---|---|--- 16 | 数据存储|传统顺序数据储存 |传统顺序数据储存|表空间存储方式|表空间存储方式||| 17 | 事务支持|不支持 | 支持|支持|支持|不支持|不支持| 18 | 外键|不支持 | 不支持|支持|支持|不支持|不支持| 19 | 全文检索|支持 | 支持|5.6 之后支持|5.6 之后支持||| 20 | 锁级别|表级锁 | 表级锁|行级锁|行级锁|表级锁|行级锁| 21 | Count 速度|快 | 快|慢|慢||| 22 | 适合业务|读多写少、单表数据量小于 1 KW |读多写少、单表数据量小于 1 KW|读写均衡、数据量不限|读写均衡、数据量不限||| 23 | 可用版本|MySQL、MariaDB、Percona | MariaDB|MySQL、MariaDB、Percona|MariaDB、Percona||| 24 | 其他说明|传统顺序索引数据库,适合读多写少小数据量业务 | MyISAM 增强版,性能更好|适合高压力高性能的业务模型|InnoDB 增强版||| 25 | 26 | ## MySQL 索引定义 27 | 28 | MySQL 的索引(Index)是帮助 MySQL 高效获取数据的数据结构 29 | 30 | 31 | ## MySQL 索引类型 32 | 33 | - Normal 34 | - 普通索引,对存储的数值没有任何限制 35 | - Unique 36 | - 唯一索引,不允许数值重复,但允许空值 37 | - Primary 38 | - 主键索引,是一种特殊的唯一索引,不允许存在数值重复或空值 39 | - Spatial(R-Tree) 40 | - GIS 相关空间查询使用索引数据结构。 41 | - Fulltext 42 | - 主要用于全文检索,目前只有 MyISAM 和 MySQL 5.6 + 的 InnoDB 支持;目前只支持英文。 43 | 44 | 45 | 46 | ## MySQL 索引方法 47 | 48 | - B+Tree 49 | - 可用于排序的索引数据结构,可应用于 =,>,< 等各个范围查询,并且可以排序 50 | - 时间复杂度:O(log2N) 51 | - Hash 52 | - 只能用于 =,IN 等操作,无法进行范围操作,Key 冲突严重情况下可能性能比 B-Tree 低下 53 | - 时间复杂度:O(1) 54 | 55 | 56 | 57 | ## MyISAM 索引结构:B+ Tree 58 | 59 | - MyISAM 主索引和辅助索引 60 | - 区别在于:主索引 Key 是唯一;辅助索引 Key 可以重复 61 | - MyISAM 为非聚簇索引 62 | 63 | 64 | ## InnoDB 索引结构:B+ Tree 65 | 66 | - InnoDB 主索引和辅助索引 67 | - InnoDB 的主索引是聚簇索引,数据和 Key 本身都会存储在 B+Tree 的叶子节点 68 | - InnoDB 的辅助索引本身主要是记录主索引的 Key,最终查找数据还是从辅助索引再去主索引查找 69 | - InnoDB 为聚簇索引 70 | 71 | ## 存储引擎及版本选择 72 | 73 | 结论: 74 | - 使用 InnoDB 于生产环境 75 | - 使用 MySQL 5.5+ 于生产环境 76 | 77 | 原因: 78 | - 更稳定可靠的数据存储和索引结构;整个存储引擎设计思想更可靠先进,接近于Oracle、SQL Server级别的数据库(有兴趣可以去阅读源码了解细节) 79 | - 更多可靠特性支持,比如事务、外键等支持(支付等关键领域事务非常重要) 80 | - 运行更稳定,不论读写数据的量级,都能够保证比较稳定的性能响应 81 | - 更好地崩溃恢复机制,特别利用一些Percona的一些工具,更有效运维InnoDB 82 | - MySQL 5.5+ 对比 MySQL 5.1.x 总体功能和性能提升太多,改进太多 83 | -------------------------------------------------------------------------------- /博客文章/学习-Javascript-之变量.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 学习 Javascript 之变量 3 | date: 2017-03-23 10:16:35 4 | tags: Javascript 5 | category: Javascript 6 | --- 7 | 8 | ## 变量的声明 9 | 10 | ```js 11 | /* 声明变量 */ 12 | var x = 3.14; 13 | var y; 14 | var z = x + y; 15 | 16 | var a = 1, b = 2, c = true; 17 | var foo = "Hello" + "World"; 18 | var bar = 2 > 1 ? 2 != 1 : 1 > 2 ; 19 | 20 | /* 声明常量 */ 21 | const PI = 3.1415; 22 | 23 | /* ES6 - 声明变量 */ 24 | let a = 10; 25 | let [a, b, c] = [1, 2, 3]; 26 | ``` 27 | 28 | ## 变量的声明作用域 29 | 30 | - var 31 | - 在全局作用域内有效 32 | - let 33 | - 只在声明所在的块级作用域内有效 34 | - const 35 | - 只在声明所在的块级作用域内有效 36 | 37 | ## 变量的声明陷阱 38 | 39 | - 变量提升 40 | - 变量无法在 let 命令声明之前使用,否则报错 41 | - 变量可以在 var 命令声明之前使用,值为 undefined 42 | - 暂时性死区 43 | - 在代码块内,使用 let 命令声明变量之前,该变量都是不可用的 44 | - 重复声明 45 | - var 命令允许在相同作用域内,重复声明一个变量 46 | - let 命令不允许在相同作用域内,重复声明一个变量 47 | 48 | ## 解构赋值 49 | ```js 50 | /* 数组的解构赋值 */ 51 | let [foo, [[bar], baz]] = [1, [[2], 3]]; 52 | 53 | /* 对象的解构赋值 */ 54 | let { foo, bar } = { foo: "aaa", bar: "bbb" }; 55 | 56 | /* 字符串的解构赋值 */ 57 | const [a, b, c, d, e] = 'hello'; 58 | 59 | /* 函数的解构赋值 */ 60 | function add([x, y]){ 61 | return x + y; 62 | } 63 | add([1, 2]); // 3 64 | ``` 65 | 66 | 变量的解构赋值用途如下: 67 | 68 | - 交换变量的值 69 | - 从函数返回多个值 70 | - 函数参数的定义 71 | - 提取 JSON 数据 72 | - 函数参数的默认值 73 | - 遍历 Map 结构 74 | - 输入模块的指定方法 75 | -------------------------------------------------------------------------------- /博客文章/学习-Javascript-之类型.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 学习 Javascript 之类型 3 | date: 2017-03-23 10:17:24 4 | tags: Javascript 5 | category: Javascript 6 | --- 7 | -------------------------------------------------------------------------------- /博客文章/学习-UML(一).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 学习 UML(一) 3 | date: 2016-12-08 17:27:41 4 | tags: UML 5 | category: UML 6 | --- 7 | 8 | ## UML 是什么 9 | UML,全称是 Unified Modeling Language,译为统一建模语言,是面向对象软件的标准化建模语言。UML 因其简单、统一的特点,而且能表达软件设计中的动态和静态信息,目前已成为可视化建模语言的工业标准。 10 | 11 | 12 | ## UML 的种类 13 | 14 | UML 2.5 包含的图形如下,一共 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 | -------------------------------------------------------------------------------- /博客文章/学习-UML(二).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 学习 UML(二) 3 | date: 2017-02-17 17:36:39 4 | tags: UML 5 | category: UML 6 | --- 7 | 8 | ## 类图 9 | 10 | 类图(class diagram)只是 UML 的一部分,但它们可能是最常用的,因为类图在描述面向对象关系时非常有用。 11 | 12 | #### 类、抽象类、接口 13 | 14 | 类是类图的主要部分。类用带有类名的方框来描述,如下图所示: 15 | 16 | ![](http://olnw31eni.bkt.clouddn.com/uml%E7%B1%BB.png) 17 | 18 | 抽象类通常使用斜体的类名,或者增加 `{abstract}` 到类名下来表示,如下图所示: 19 | 20 | ![](http://olnw31eni.bkt.clouddn.com/uml%E6%8A%BD%E8%B1%A1%E7%B1%BB.png) 21 | 22 | 接口的定义方式和类相同,但接口必须增加 `<>` 到类名上来表示,如下图所示: 23 | 24 | ![](http://olnw31eni.bkt.clouddn.com/uml%E6%8E%A5%E5%8F%A3.png) 25 | 26 | #### 属性 27 | 28 | 属性用于描述一个类的属性,属性直接列在类名下面的格子中。属性使用符号来表示类属性的可见性,其中 `+` 对应 `public` 、`-` 对应 `private`、`#` 对应 `protected`,如下图所示: 29 | 30 | ![](http://olnw31eni.bkt.clouddn.com/uml%E5%B1%9E%E6%80%A7.png) 31 | 32 | #### 操作 33 | 34 | 操作用于描述类方法,操作表示类方法可见性的办法与属性一致。类方法有参数,则包含在括号之中;类方法有返回类型,则用冒号来描述,如下图所示: 35 | 36 | ![](http://olnw31eni.bkt.clouddn.com/uml%E6%93%8D%E4%BD%9C.png) 37 | 38 | #### 继承 39 | 40 | UML 一般用「泛化」来描述继承关系。这个关系用从子类到父类的一条线来标识,线的顶端有一个空心闭合箭头,如下图所示: 41 | 42 | ![](http://olnw31eni.bkt.clouddn.com/uml%E7%BB%A7%E6%89%BF.png) 43 | 44 | #### 实现 45 | 46 | UML 用「实现」来描述接口和实现接口的类之间的关系。这个关系用从实现接口的类到接口的一条虚线来标识,线的顶端有一个空心闭合箭头,如下图所示: 47 | 48 | ![](http://olnw31eni.bkt.clouddn.com/uml%E5%AE%9E%E7%8E%B0.png) 49 | 50 | #### 关联 51 | 52 | 当一个类的属性保存了对另一个类的一个实例或多个实例的引用时,就产生了关联。类之间的关联有单向关联、双向关联和多个引用关联,如下图所示: 53 | 54 | ![](http://olnw31eni.bkt.clouddn.com/uml%E5%85%B3%E8%81%94.png) 55 | 56 | #### 聚合和组合 57 | 58 | 聚合和组合都描述了一个类长期持有其他类的一个或多个实例的情况。通过聚合或组合,被引用的对象实例成为引用对象的一部分。 59 | 60 | 聚合的情况下,被包含对象是容器的一个核心部分,但是它们也可以同时被其他对象所包含。聚合关系用一条空心菱形开头的线来说明。 61 | 62 | 组合则是一个更强的关系。在组合中,被包含对象只能被它的容器所引用。当容器被删除时,它也应该被删除。组合关系可以用类聚合关系的方式描述,但是菱形必须是实心的。 63 | 64 | ![](http://olnw31eni.bkt.clouddn.com/uml%E8%81%9A%E5%90%88.png) 65 | ![](http://olnw31eni.bkt.clouddn.com/uml%E7%BB%84%E5%90%88.png) 66 | 67 | 68 | #### 描述使用 69 | 70 | 一个对象使用另一个对象的关系在 UML 中被描述为一个依赖关系。一个被使用的类可以作为类方法的参数传递或者作为方法调用的结果得到。 71 | 72 | ![](http://olnw31eni.bkt.clouddn.com/uml%E6%8F%8F%E8%BF%B0%E4%BD%BF%E7%94%A8.png) 73 | 74 | #### 使用注解 75 | 76 | 类图可以捕捉到系统的结构,但类图并不能解释类处理任务的过程,可以通过使用注解来补充说明。注解由一个折角的方框组成。它通常包含伪代码片段。注解使类图变得易于理解。 77 | 78 | ![](http://olnw31eni.bkt.clouddn.com/uml%E6%B3%A8%E9%87%8A.png) 79 | -------------------------------------------------------------------------------- /博客文章/打造扛得住的-MySQL-数据库架构.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 打造扛得住的 MySQL 数据库架构 3 | date: 2016-10-20 20:57:11 4 | tags: MySQL 5 | category: MySQL 6 | --- 7 | 8 | ## 第1章 实例和故事 9 | 10 | ### 1-1 什么决定了电商双11大促的成败 11 | 12 | 电商公司的服务器能够承受住双十一期间高并发的访问量,为用户提供稳定高速的购物体验。 13 | 14 | ### 1-2 在双11大促中的数据库服务器 15 | 16 | 数据库架构: 17 | 18 | - 1 台 MySQL 主服务器 19 | - 15 台 MySQL 从服务器 20 | 21 | 当时没有部署主从复制组件,一旦主服务器出现故障,很难自动进行故障切换,必须由 DBA 进行手动操作。过多的从服务器,对主服务器的网卡容量也是一个不小的挑战。 22 | 23 | 数据库性能依据参数: 24 | 25 | - QPS 26 | - TPS 27 | - 并发量 28 | - CPU 使用率 29 | - 磁盘 IO 30 | 31 | ### 1-3 在大促中什么影响了数据库性能 32 | 33 | 影响数据库性能的因素 34 | 35 | - SQL 查询速度 36 | - 服务器硬件 37 | - 网卡流量 38 | - 磁盘 IO 39 | 40 | 数据库性能常见的风险: 41 | 42 | - 效率低下的 SQL 43 | - QPS:每秒钟处理的查询量 44 | - 10ms 处理 1个 SQL,1s 处理 100个 SQL,QPS <= 100 45 | - 100ms 处理 1个 SQL,1s 处理 10个 SQL,QPS <= 10 46 | - TPS:每秒钟处理的事务数 47 | - 大量的并发和超高的 CPU 使用率 48 | - 大量的并发会导致数据库连接数被占满 49 | - 超高的 CPU 使用率会导致服务器因 CPU 资源耗尽而出现宕机 50 | - 磁盘 IO 51 | - 磁盘 IO 性能突然下降(使用更快的磁盘设备) 52 | - 其它大量消耗磁盘性能的计划任务(调整计划任务,做好磁盘维护) 53 | - 网卡 IO 54 | - 当网卡 IO 被占满时,(千兆网卡 1000 Mb/8=100 MB)会出现无法连接数据库的情况。 55 | - 如何避免无法连接数据库的情况 56 | - 减少从服务器的数量 57 | - 进行分级缓存 58 | - 避免使用 `select *` 进行查询 59 | - 分离业务网络和服务器网络 60 | - 大表 61 | - 什么样的表可以称之为大表 62 | - 记录行数巨大,单表超过千万行 63 | - 表数据文件巨大,表数据文件超过 10 G 64 | - 大表对查询的影响 65 | - 慢查询:很难在一定的时间内过滤出所需要的数据 66 | - 区分度低的数据在查询的时候会产生大量的磁盘 IO,会降低磁盘的效率,导致查询效率低下,出现大量的慢查询。 67 | - 大表对 DDL 操作的影响 68 | - 建立索引需要很长的时间 69 | - MySQL 版本 < 5.5,建立索引会锁表 70 | - MySQL 版本 >= 5.5,虽然不会锁表但会引起主从延迟 71 | - 修改表结构需要长时间锁表 72 | - 会造成长时间的主从延迟 73 | - 影响正常的数据操作 74 | - 解决方案 75 | - 分库分表,把一张大表分成多个小表 76 | - 难点 77 | - 分表主键的选择 78 | - 分表后跨分区数据的查询和统计 79 | - 大表的历史数据归档(减少对前后端业务的影响) 80 | - 难点 81 | - 归档时间点的选择 82 | - 如何进行归档操作 83 | - 大事务 84 | - 事务的概念 85 | - 事务是数据库系统区别于其它一切文件系统的重要特性之一 86 | - 事务是一组具有原子性的 SQL 语句,或是一个独立的工作单元 87 | - 事务的特性 88 | - 原子性 89 | - 一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败,对于一个事务来说,不可能只执行其中的一部分操作。 90 | - 一致性 91 | - 一致性是指事务将数据库从一种一致性状态转换到另外一种一致性状态。在事务开始之前和事务结束后,数据库中的数据完整性没有被破坏。 92 | - 隔离性 93 | - 隔离性要求一个事务对数据库中数据的修改,在未提交完成前对于其它事务是不可见的。 94 | - 隔离性的类型 95 | - 未提交读(Read Uncommited) 96 | - 已提交读(Read Commited) 97 | - 可重复读(Repeatable Read) 98 | - 可串行化(Serialization) 99 | - 持久性 100 | - 一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,已经提交的修改数据也不会丢失 101 | - 什么是大事务 102 | - 定义:运行时间比较长,操作的数据比较多的事务 103 | - 风险: 104 | - 锁定太多的数据,造成大量的阻塞和锁超时 105 | - 回滚时所需时间比较长 106 | - 执行时间长,容易造成主从延迟 107 | - 处理: 108 | - 避免一次处理太多的数据 109 | - 移出不必要在事务中的 SELECT 操作 110 | 111 | ## 第2章 什么影响了MySQL性能 112 | 113 | ## 第3章 MySQL基准测试 114 | 115 | ## 第4章 MySQL数据库结构优化 116 | 117 | ## 第5章 MySQL高可用架构设计 118 | 119 | ## 第6章 数据库索引优化 120 | 121 | ## 第7章 SQL查询优化 122 | 123 | ## 第8章 数据库的分库分表 124 | 125 | ## 第9章 数据库监控 126 | 127 | -------------------------------------------------------------------------------- /博客文章/查看-Eloquent-模型关联时执行的-SQL-语句.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 查看 Eloquent 模型关联时执行的 SQL 语句 3 | date: 2016-09-01 20:34:56 4 | tags: Laravel 5 | category: PHP 6 | --- 7 | 8 | 本文目的是查看 Eloquent 在执行模型关联时运行的 SQL 语句 9 | 10 | 11 | ## 环境 12 | 13 | - PHP 7 14 | - Laravel 5.1 15 | 16 | 17 | ## 简介 18 | 19 | 数据表之间经常会互相进行关联。例如,一篇博客文章可能会有多条评论,或是一张订单可能对应一个下单客户。Eloquent 让管理和处理这些关联变得很容易,同时也支持多种类型的关联: 20 | 21 | - 一对一 22 | - 一对多 23 | - 多对多 24 | - 远层一对多 25 | - 多态关联 26 | - 多态多对多关联 27 | 28 | 接下来,我会根据公司项目的数据库结构,进行举例说明 29 | 30 | ## 准备 31 | 32 | 新增控制器 33 | ```shell 34 | php artisan make:controller EloquentController 35 | ``` 36 | 37 | 新增路由 38 | ```php 39 | Route::get('/', function () { 40 | return view('welcome'); 41 | }); 42 | 43 | // 一对一 44 | Route::get('eloquent/one-to-one', [ 45 | 'as' => 'eloquent.one.to.one', 46 | 'uses' => 'EloquentController@oneToOne', 47 | ]); 48 | 49 | // 一对多 50 | Route::get('eloquent/one-to-many', [ 51 | 'as' => 'eloquent.one.to.many', 52 | 'uses' => 'EloquentController@oneToMany', 53 | ]); 54 | 55 | // 多对多 56 | Route::get('eloquent/many-to-many', [ 57 | 'as' => 'eloquent.many.to.many', 58 | 'uses' => 'EloquentController@manyToMany', 59 | ]); 60 | 61 | // 远程一对多 62 | Route::get('eloquent/has-many-through', [ 63 | 'as' => 'eloquent.has.many.through', 64 | 'uses' => 'EloquentController@hasManyThrough', 65 | ]); 66 | 67 | // 多态关联 68 | Route::get('eloquent/polymorphic-relations', [ 69 | 'as' => 'eloquent.polymorphic.relations', 70 | 'uses' => 'EloquentController@polymorphicRelations', 71 | ]); 72 | 73 | // 多对多多态管理 74 | Route::get('eloquent/many-to-many-polymorphic-relations', [ 75 | 'as' => 'eloquent.many.to.many.polymorphic.relations', 76 | 'uses' => 'EloquentController@manyToManyPolymorphicRelations', 77 | ]); 78 | ``` 79 | 80 | ## 一对一 81 | 82 | 场景是如每一名加盟商都对应着一份加盟商配置 83 | 84 | 新增模型 85 | ``` 86 | php artisan make:model Models/League 87 | php artisan make:model Models/LeagueConfig 88 | ``` 89 | 90 | 在 `League` 模型添加代码如下 91 | ```php 92 | // League.php 一名加盟商有一份配置 93 | public function config() 94 | { 95 | return $this->hasOne('App\Models\LeagueConfig'); 96 | } 97 | ``` 98 | 99 | 在 `LeagueConfig` 模型添加代码如下 100 | ``` 101 | // LeagueConfig.php 一份配置对应一名加盟商 102 | public function league() 103 | { 104 | return $this->hasOne('App\Models\League'); 105 | } 106 | ``` 107 | 108 | 控制器新增代码如下 109 | ```php 110 | public function oneToOne(Request $request) 111 | { 112 | DB::enableQueryLog(); 113 | $data = League::find(8)->config; 114 | foreach (DB::getQueryLog() as $sql) { 115 | dump($sql['query']); 116 | } 117 | dump($data); 118 | } 119 | ``` 120 | 121 | 查看 SQL 122 | ```sql 123 | SELECT 124 | * 125 | FROM 126 | `z_league` 127 | WHERE 128 | `z_league`.`id` = 8 129 | LIMIT 1; 130 | 131 | 132 | SELECT 133 | * 134 | FROM 135 | `z_leagueconfig` 136 | WHERE 137 | `z_leagueconfig`.`league_id` = 8 138 | AND `z_leagueconfig`.`league_id` IS NOT NULL 139 | LIMIT 1; 140 | ``` 141 | 142 | ## 一对多 143 | 144 | 场景是仓库一种 SKU 的水果能被多名加盟商进货 145 | 146 | 新增模型 147 | ``` 148 | php artisan make:model Models/Product 149 | php artisan make:model Models/ProductLeague 150 | ``` 151 | 152 | `Product` 模型中添加代码 153 | ```php 154 | public function league() 155 | { 156 | return $this->hasMany('App\Models\ProductLeague', 'pid'); 157 | } 158 | ``` 159 | 160 | 控制器新增代码如下: 161 | ``` 162 | public function oneToMany(Request $request) 163 | { 164 | DB::enableQueryLog(); 165 | $data = Product::find(3062)->league; 166 | foreach (DB::getQueryLog() as $sql) { 167 | dump($sql['query']); 168 | } 169 | 170 | dump($data); 171 | } 172 | ``` 173 | 174 | 查看 SQL 175 | ```sql 176 | SELECT 177 | * 178 | FROM 179 | `z_product` 180 | WHERE 181 | `z_product`.`id` = 3062 182 | LIMIT 1; 183 | 184 | SELECT 185 | * 186 | FROM 187 | `z_productleague` 188 | WHERE 189 | `z_productleague`.`pid` = 3062 190 | AND `z_productleague`.`pid` IS NOT NULL; 191 | ``` 192 | 193 | ## 多对多 194 | 195 | 场景是一名用户使用多张不同种类的优惠券,一种优惠券被不同的用户使用 196 | 197 | 新增模型 198 | ```shell 199 | php artisan make:model Models/User 200 | php artisan make:model Models/Voucher 201 | php artisan make:model Models/VoucherRecord 202 | ``` 203 | 204 | `User` 模型添加代码如下 205 | ```php 206 | public function voucher() 207 | { 208 | return $this->belongsToMany('App\Models\Voucher', 'z_voucher_record', 'uid', 'vid'); 209 | } 210 | ``` 211 | 212 | `Voucher` 模型添加代码如下 213 | ```php 214 | public function user() 215 | { 216 | return $this->belongsToMany('App\Models\User', 'z_voucher_record', 'vid', 'uid'); 217 | } 218 | ``` 219 | 220 | 控制器新增代码如下 221 | ``` 222 | public function manyToMany(Request $request) 223 | { 224 | DB::enableQueryLog(); 225 | $vouchers = User::find(412115)->voucher; 226 | foreach (DB::getQueryLog() as $sql) { 227 | dump($sql['query']); 228 | } 229 | dump($vouchers); 230 | 231 | DB::enableQueryLog(); 232 | $user = Voucher::find(395)->user; 233 | foreach (DB::getQueryLog() as $sql) { 234 | dump($sql['query']); 235 | } 236 | dump($user); 237 | } 238 | ``` 239 | 240 | 查看 SQL 241 | ```sql 242 | # 查询 ID 为 42115 的用户使用过的优惠券 243 | SELECT 244 | * 245 | FROM 246 | `z_user` 247 | WHERE 248 | `z_user`.`id` = 412115; 249 | 250 | LIMIT 1 SELECT 251 | `z_voucher`.*, `z_voucher_record`.`uid` AS `pivot_uid` , 252 | `z_voucher_record`.`vid` AS `pivot_vid` 253 | FROM 254 | `z_voucher` 255 | INNER JOIN `z_voucher_record` ON `z_voucher`.`id` = `z_voucher_record`.`vid` 256 | WHERE 257 | `z_voucher_record`.`uid` = 412115; 258 | 259 | # 查询 ID 为 395 的优惠券被哪些用户使用过 260 | 261 | SELECT 262 | * 263 | FROM 264 | `z_voucher` 265 | WHERE 266 | `z_voucher`.`id` = 395; 267 | 268 | LIMIT 1 SELECT 269 | `z_user`.*, `z_voucher_record`.`vid` AS `pivot_vid` , 270 | `z_voucher_record`.`uid` AS `pivot_uid` 271 | FROM 272 | `z_user` 273 | INNER JOIN `z_voucher_record` ON `z_user`.`id` = `z_voucher_record`.`uid` 274 | WHERE 275 | `z_voucher_record`.`vid` = 395; 276 | ``` 277 | 278 | ## 远程一对多 279 | 280 | 新增模型 281 | ``` 282 | php artisan make:model Models/Order 283 | ``` 284 | 285 | `Order` 模型新增代码如下 286 | ``` 287 | public function order() 288 | { 289 | return $this->hasManyThrough('App\Models\Order', 'App\Models\User', 'league_id', 'uid'); 290 | } 291 | ``` 292 | 293 | 控制器新增代码如下 294 | ``` 295 | public function hasManyThrough(Request $request) 296 | { 297 | DB::enableQueryLog(); 298 | $data = League::find(20)->order; 299 | foreach (DB::getQueryLog() as $sql) { 300 | dump($sql['query']); 301 | } 302 | 303 | dump($data); 304 | } 305 | ``` 306 | 307 | 查看 SQL 308 | ```sql 309 | # 查询 ID 为 20 的加盟商旗下用户的商品订单 310 | SELECT 311 | * 312 | FROM 313 | `z_league` 314 | WHERE 315 | `z_league`.`id` = 20 316 | LIMIT 1; 317 | 318 | SELECT 319 | `z_order`.*, `z_user`.`league_id` 320 | FROM 321 | `z_order` 322 | INNER JOIN `z_user` ON `z_user`.`id` = `z_order`.`uid` 323 | WHERE 324 | `z_user`.`league_id` = 20; 325 | ``` 326 | 327 | ## 多态关联 328 | 329 | 新增模型如下 330 | ```shell 331 | php artisan make:model Models/Staff 332 | php artisan make:model Models/photos 333 | php artisan make:model Models/Products 334 | ``` 335 | 336 | 控制器新增代码如下 337 | ```php 338 | public function polymorphicRelations(Request $request) 339 | { 340 | DB::enableQueryLog(); 341 | $staff = Staff::find(8)->photos; 342 | foreach (DB::getQueryLog() as $sql) { 343 | dump($sql['query']); 344 | } 345 | 346 | dump($staff); 347 | 348 | DB::enableQueryLog(); 349 | $products = Products::find(1)->photos; 350 | foreach (DB::getQueryLog() as $sql) { 351 | dump($sql['query']); 352 | } 353 | 354 | dump($products); 355 | } 356 | ``` 357 | 358 | 查看 SQL 359 | ```sql 360 | # 查看 ID 为 8 的职员的所有照片 361 | SELECT 362 | * 363 | FROM 364 | `eloquent_staff` 365 | WHERE 366 | `eloquent_staff`.`id` = 8 367 | LIMIT 1; 368 | 369 | SELECT 370 | * 371 | FROM 372 | `eloquent_photos` 373 | WHERE 374 | `eloquent_photos`.`imageable_id` = 8 375 | AND `eloquent_photos`.`imageable_id` IS NOT NULL 376 | AND `eloquent_photos`.`imageable_type` = "App\Models\Staff"; 377 | 378 | # 查看 ID 为 1 的产品的所有图片 379 | SELECT 380 | * 381 | FROM 382 | `eloquent_products` 383 | WHERE 384 | `eloquent_products`.`id` = 1 385 | LIMIT 1; 386 | 387 | SELECT 388 | * 389 | FROM 390 | `eloquent_photos` 391 | WHERE 392 | `eloquent_photos`.`imageable_id` = 1 393 | AND `eloquent_photos`.`imageable_id` IS NOT NULL 394 | AND `eloquent_photos`.`imageable_type` = "App\Models\Products"; 395 | ``` 396 | 397 | ## 多态多对多关联 398 | 399 | 新增模型如下 400 | ```php 401 | php artisan make:model Models/Tag 402 | php artisan make:model Models/Post 403 | php artisan make:model Models/Video 404 | ``` 405 | 406 | 控制器新增代码如下 407 | ```php 408 | public function manyToManyPolymorphicRelations(Request $request) 409 | { 410 | DB::enableQueryLog(); 411 | $post_tags = Post::find(8)->tags; 412 | foreach (DB::getQueryLog() as $sql) { 413 | dump($sql['query']); 414 | } 415 | 416 | dump($post_tags); 417 | 418 | DB::enableQueryLog(); 419 | $video_tags = Video::find(8)->tags; 420 | foreach (DB::getQueryLog() as $sql) { 421 | dump($sql['query']); 422 | } 423 | 424 | dump($video_tags); 425 | 426 | DB::enableQueryLog(); 427 | $posts = Tag::find(4)->posts; 428 | foreach (DB::getQueryLog() as $sql) { 429 | dump($sql['query']); 430 | } 431 | 432 | dump($posts); 433 | 434 | DB::enableQueryLog(); 435 | $videos = Tag::find(4)->videos; 436 | foreach (DB::getQueryLog() as $sql) { 437 | dump($sql['query']); 438 | } 439 | 440 | dump($videos); 441 | } 442 | ``` 443 | 444 | 查看 SQL 445 | ```sql 446 | # 查询 ID 为 8 的文章所使用的标签 447 | SELECT 448 | * 449 | FROM 450 | `eloquent_posts` 451 | WHERE 452 | `eloquent_posts`.`id` = 8 453 | LIMIT 1; 454 | 455 | SELECT 456 | `eloquent_tags`.*, `eloquent_taggables`.`taggable_id` AS `pivot_taggable_id` , 457 | `eloquent_taggables`.`tag_id` AS `pivot_tag_id` 458 | FROM 459 | `eloquent_tags` 460 | INNER JOIN `eloquent_taggables` ON `eloquent_tags`.`id` = `eloquent_taggables`.`tag_id` 461 | WHERE 462 | `eloquent_taggables`.`taggable_id` = 8 463 | AND `eloquent_taggables`.`taggable_type` = "App\Models\Post"; 464 | 465 | # 查询 ID 为 8 的视频所使用的标签 466 | SELECT 467 | * 468 | FROM 469 | `eloquent_videos` 470 | WHERE 471 | `eloquent_videos`.`id` = 8 472 | LIMIT 1; 473 | 474 | SELECT 475 | `eloquent_tags`.*, `eloquent_taggables`.`taggable_id` AS `pivot_taggable_id` , 476 | `eloquent_taggables`.`tag_id` AS `pivot_tag_id` 477 | FROM 478 | `eloquent_tags` 479 | INNER JOIN `eloquent_taggables` ON `eloquent_tags`.`id` = `eloquent_taggables`.`tag_id` 480 | WHERE 481 | `eloquent_taggables`.`taggable_id` = 8 482 | AND `eloquent_taggables`.`taggable_type` = "App\Models\Video"; 483 | 484 | # 查询 ID 为 4 的标签下的文章 485 | SELECT 486 | * 487 | FROM 488 | `eloquent_tags` 489 | WHERE 490 | `eloquent_tags`.`id` = 4 491 | LIMIT 1; 492 | 493 | SELECT 494 | `eloquent_posts`.*, `eloquent_taggables`.`tag_id` AS `pivot_tag_id` , 495 | `eloquent_taggables`.`taggable_id` AS `pivot_taggable_id` 496 | FROM 497 | `eloquent_posts` 498 | INNER JOIN `eloquent_taggables` ON `eloquent_posts`.`id` = `eloquent_taggables`.`taggable_id` 499 | WHERE 500 | `eloquent_taggables`.`tag_id` = 4 501 | AND `eloquent_taggables`.`taggable_type` = "App\Modes\Post"; 502 | 503 | # 查询 ID 为 4 的标签下的视屏 504 | 505 | SELECT 506 | * 507 | FROM 508 | `eloquent_tags` 509 | WHERE 510 | `eloquent_tags`.`id` = 4 511 | LIMIT 1; 512 | 513 | SELECT 514 | `eloquent_videos`.*, `eloquent_taggables`.`tag_id` AS `pivot_tag_id` , 515 | `eloquent_taggables`.`taggable_id` AS `pivot_taggable_id` 516 | FROM 517 | `eloquent_videos` 518 | INNER JOIN `eloquent_taggables` ON `eloquent_videos`.`id` = `eloquent_taggables`.`taggable_id` 519 | WHERE 520 | `eloquent_taggables`.`tag_id` = 4 521 | AND `eloquent_taggables`.`taggable_type` = "App\Models\Video"; 522 | ``` 523 | --------------------------------------------------------------------------------