diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9ff5125 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.md linguist-language=php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffc0098 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/node_modules +/vendor +/.idea +/.vscode +/.vagrant +.env +/_book \ No newline at end of file diff --git a/Cache/Redis.md b/Cache/Redis.md index 0e9b013..54cfc97 100644 --- a/Cache/Redis.md +++ b/Cache/Redis.md @@ -2,7 +2,9 @@ redis是一个开源的支持多种数据类型的key=>value的存储数据库。支持字符串、列表、集合、有序集合、哈希五种类型 +图片过大,请下载到本地打开 +![redis](redis.png) ### redis 和memcache区别 @@ -37,3 +39,10 @@ redis是一个开源的支持多种数据类型的key=>value的存储数据库 > hset、hget、hmget、hmset、hkeys、hlen、hsetnx、hvals +### redis 各种类型的场景使用 + +- string 就是存储简单的key=>value的字符串 +- list 使用场景。做先进先出的消费队列 +- set 进行集合过滤重复元素 +- zset 有序集合,排行榜 TOP N +- hash 适合存储一组数据,比如用户的信息 以用户id为键,里面记录用户的昵称等信息。 \ No newline at end of file diff --git a/Cache/redis.png b/Cache/redis.png new file mode 100644 index 0000000..8b858ba Binary files /dev/null and b/Cache/redis.png differ diff --git a/Linux/AWK.md b/Linux/AWK.md new file mode 100644 index 0000000..c920209 --- /dev/null +++ b/Linux/AWK.md @@ -0,0 +1,129 @@ +## AWK题目练习 + +### awk工作原理 + +## AWK工作原理 + +- 第一步:执行BEGIN{action;… }语句块中的语句 +- 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{action;… }语句块,它逐行扫描文件,从第 + 一行到最后一行重复这个过程,直到文件全部被读取完毕。 +- 第三步:当读至输入流末尾时,执行END{action;…}语句块BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行, awk读取的每一行都会执行该语句块 + +### awk 内置变量 + +ARGC 命令行参数个数 + +ARGV 命令行参数排列 + +ENVIRON 支持队列中系统环境变量的使用 + +FILENAME awk浏览文件名 + +FNR 浏览文件的记录数 + +FS 设置输入域分隔符,等价于命令行-F选项 + +NF 浏览记录的域个数 + +NR 已读的记录数 + +OFS 输出域分隔符 + +ORS 输出例句分隔符 + +RS 控制记录分隔符 + +1. 打印出/etc/passwd中个的第一个域,并在前面追加"账号" + +```shell +cat /etc/passwd | awk -F ":" '{print "账号"$1}' +``` + +2. 打印出/etc/passwd 第三个域和第四个域 + +```shell +cat /etc/passwd | awk -F ":" '{print $3,$4}' +``` + +3. 匹配/etc/passwd 第三域大于100的显示出完整信息 + +```shell +cat /etc/passwd | awk -F ":" '{if($3 > 100) {print $0}}' +``` + +4. 打印行号小于15的,并且最后一域匹配bash的信息. + +NR表示行号。NF表示最后一个域 ~ 正则匹配符号。 // 正则表达式开始和结束符号 + +```shell +cat /etc/passwd | awk -F ":" '{if($NR < 15 && $NF~/bash/) {print $0}}' +``` + +5. 打印出第三域数字之和 + +```SHELL +awk -F ":" 'BEGIN{sum =0} {sum = sum+$3} END {print sum}' +``` + +6. 请匹配passwd最后一段域bash结尾的信息,有多少条 + +```shell +awk -F ":" '{if( $NF~/bash/) {i++}} END {print i }' +``` + +7. 请同时匹配passwd文件中,带mail和bash的关键字的信息 + +```shell +cat /etc/passwd | awk -F ":" '{if( $NF~/bash/ || $NF~/mail/) {print $0}} ' +``` + +8. 统计/etc/fstab文件中每个文件系统类型出现的次数 + + /^UUID/:模式匹配以UUID开头的行  + + fs[$3]++:定义fs[]为关联数组下标是每条记录的第3个字段,数组的值是出现的次数 + + for(i in fs){print i,fs[i]}:在每条记录都处理完之后,用for循环遍历数组,打印下标(文件类型)和数组元素值(文件类型出现的次数) + +```shell +awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab +``` + +### nginx日志分析 + +日志格式 + +`'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"' ` + +**日志记录:**27.189.231.39 - - [09/Apr/2016:17:21:23 +0800] "GET /Public/index/images/icon_pre.png HTTP/1.1" 200 44668 "http://www.test.com/Public/index/css/global.css" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36" "-" + +- 统计日志最多的10个IP + +```shell +awk '{arr[$1]++} END {for(i in arr) {print i}}' access.log | sort -k1 -nr | head -n10 +``` + +- 统计日志访问次数大于100次的IP + +```shell +awk '{arr[$1]++} END{for (i in arr) {if(arr[i] > 100){print i}}}' access.log +``` + +- 统计2016年4月9日内访问最多的10个ip + +```shell +awk '$4>="[09/Apr/2016:00:00:00" && $4<="[09/Apr/2016:23:59:59" {arr[i]++} END{print arr[i]}' |sort -k1 -nr|head -n10 +``` + +- 统计访问最多的十个页面 + +```shell + awk '{a[$7]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log +``` + +- 统计访问状态为404的ip出现的次数 + +```shell +awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log +``` + diff --git "a/Linux/LinuxIO\346\250\241\345\236\213.md" "b/Linux/LinuxIO\346\250\241\345\236\213.md" new file mode 100644 index 0000000..fd585c1 --- /dev/null +++ "b/Linux/LinuxIO\346\250\241\345\236\213.md" @@ -0,0 +1,126 @@ +## 概念说明 + +### 1. 内核空间、用户空间 + +操作系统的核心是内核,独立于其他应用程序,可以访问底层会保护的硬件,Linux**为了防止用户进程直接操作内核**,将虚拟地址空间,分成了用户空间和内核空间,用户空间就是用户进程所在的空间。 + +### 2. 进程切换 + +为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的 + +从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化: + +> 1. 保存处理机上下文,包括程序计数器和其他寄存器。 +> 2. 更新PCB信息。 +> 3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。 +> 4. 选择另一个进程执行,并更新其PCB。 +> 5. 更新内存管理的数据结构。 +> 6. 恢复处理机上下文。 + + + + ### 3. 进程的阻塞 + +> 正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。`当进程进入阻塞状态,是不占用CPU资源的`。 + +### 4. 进程缓存区、内核缓冲区 + +缓冲区的出现是为了减少频繁的系统调用,由于系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要回复之前的信息,为了减少这种耗时耗性能的调用于是出现了缓冲区。在linux系统中,每个进程有自己独立的缓冲区,叫做**进程缓冲区**,而系统内核也有个缓冲区叫做**内核缓冲区**。 + + **操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中** + +### 5. 文件描述符fd + +文件描述符(File descriptor)是计算机科学中的一个术语,`是一个用于表述指向文件的引用的抽象化概念`。 文件描述符在形式上是一个非负整数。实际上,`它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表`。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开 + +## Linx/Unix 5种IO模型 + +当一个io发生时候的,涉及到的步骤和对象 + +以网络socket的 read为例子。 + +涉及到的对象 + +- 一个是调用这个IO的process (or thread) (用户进程) +- 一个就是系统内核(kernel) + +经历的步骤 + +- 等待数据准备,比如accept(), recv()等待数据 +- 将数据从内核拷贝到进程中, 比如 accept()接受到请求,recv()接收连接发送的数据后需要复制到内核,再从内核复制到进程**用户空间** + +### 阻塞IO + +![img](https://upload-images.jianshu.io/upload_images/1446087-9522cafa9e14abd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/552) + +> 当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到**操作系统内核的缓冲区**中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会**将数据从kernel中拷贝到用户内存**,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。 + + + + ### 非阻塞IO + +![img](https://upload-images.jianshu.io/upload_images/1446087-0c604ff4a2d8dc5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/603) + + 当用户进程发出read操作时,如果kernel中的数据还没有准备好,**那么它并不会block用户进程,而是立刻返回一个error**。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回 + +### I/O 多路复用( IO multiplexing) + +IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。 + +![img](https://upload-images.jianshu.io/upload_images/1446087-3b0399b077daf0a8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/609) + +在一个调用中阻塞`select`,等待数据报套接字可读。当`select` 返回套接字可读时,我们然后调用`recvfrom` 将数据报复制到我们的应用程序缓冲区中 .使用`select`需要两次系统调用而不是一次 + +在IO multiplexing Model中,实际中,**对于每一个socket,一般都设置成为non-blocking,因为只有设置成non-blocking 才能使单个线程/进程不被阻塞(或者说锁住),可以继续处理其他socket。如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。** + +### 异步 I/O + +![img](https://upload-images.jianshu.io/upload_images/1446087-e707464b299104e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/572) + +用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了 + +### 异步、同步、阻塞、非阻塞 + +同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列 + +异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了 + +阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务 + +非阻塞调用指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回 + +异步、同步是发生在用户空间内,当用户发起一个IO的调用的时候,同步的时候,如果这个操作比较耗时间,会阻塞后面的流程 + +```php +file_get_contents("http://www.qq.com/"); +echo "end"; +``` + +调用read的操作的时候。后面的操作echo 会等待上面的结果完成,才能继续。 + + + +```php +aysnc_read("http://www.qq.com/",function($data){ + echo $data; +}) + echo "end"; +``` + +这个aysnc_read 是一个异步读的操作,当读的时候,底下的操作不会阻塞住,会先输出end。当数据到达的时候,再echo $data; + +阻塞、非阻塞、发生在内核和用户空间之间。阻塞是指操作系统会挂起进程,直到数据准备好,非阻塞、操作系统不阻塞,当前进程可以继续执行。 + +举例说明 + +- 阻塞io + +张三去书店买书,然后问书店问老板,有没有《红楼梦》,老板说我查下,这个查询的时间,比较长,然后张三啥都不能干,就在等着。直到老板告诉它,找到了。然后买了这个书,走了。张三的操作都是同步阻塞的,必须等待老板的结果,下面的操作才能执行。 + +- 非阻塞IO + +还是张三去买书,老板去查询。这是时候,张三可以玩手机,然后隔段时间问,张三问:"找到了没有",张三的进程没有被阻塞。但是这个任务是同步的,必须等待这个结果。就是老板没有告诉张三结果,张三是不能离开干其他的事。这个过程是同步非阻塞的。 + +- 异步IO + +张三去买书。然后去书店问老板有没有了。老板需要查询,张三告诉老板自己的手机号,找到了打电话给我,然后就去干其他的事了。这个过程是异步的。张三的进程没有被阻塞在这个买书的环节上。这就是异步非阻塞。 diff --git "a/Linux/Linux\345\221\275\344\273\244.md" "b/Linux/Linux\345\221\275\344\273\244.md" index e26973b..7dfbd32 100644 --- "a/Linux/Linux\345\221\275\344\273\244.md" +++ "b/Linux/Linux\345\221\275\344\273\244.md" @@ -47,6 +47,7 @@ cat a.text| less ```shell ls /proc && echo suss! || echo failed. +cat access.log >> test.log ``` ## 文本处理 @@ -134,7 +135,7 @@ sed '/^$/d' file #删除空白行 详细教程可以查看 http://awk.readthedocs.io/en/latest/chapter-one.html ```shell -awk ' BEGIN{ statements } statements2 END{ statements } ' +awk ' BEGIN{ statements } statements2 END{ statements } ' ``` 工作流程 @@ -161,11 +162,11 @@ $2:第二个字段的文本内容; awk '{print $2, $3}' file # 日志格式:'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"' #统计日志中访问最多的10个IP -awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log +awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log ``` -### 排序 port +### 排序 sort - -n 按数字进行排序 VS -d 按字典序进行排序 - -r 逆序排序 diff --git "a/Linux/Linux\345\221\275\344\273\2442.md" "b/Linux/Linux\345\221\275\344\273\2442.md" index b9f99fd..0b05ed1 100644 --- "a/Linux/Linux\345\221\275\344\273\2442.md" +++ "b/Linux/Linux\345\221\275\344\273\2442.md" @@ -2,13 +2,13 @@ 查看磁盘空间利用大小 -``` +```shell df -h ``` 查看当前目录所占空间大小 -``` +```shell du -sh ``` @@ -59,21 +59,21 @@ netstat 命令用于显示各种网络相关信息,如网络连接,路由表 列出所有端口 (包括监听和未监听的): -``` +```shell netstat -a ``` 列出所有 tcp 端口: -``` +```shell netstat -at ``` 列出所有有监听的服务状态: -``` +```shell netstat -l ``` @@ -82,7 +82,7 @@ netstat -l 缺省时free的单位为KB ```shell -$free +$ free total used free shared buffers cached Mem: 8175320 6159248 2016072 0 310208 5243680 -/+ buffers/cache: 605360 7569960 diff --git a/Linux/Nginx.md b/Linux/Nginx.md new file mode 100644 index 0000000..fac991c --- /dev/null +++ b/Linux/Nginx.md @@ -0,0 +1,269 @@ +## nginx +Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,已经渐渐取代老牌Apache 作为新的web服务器使用。 +### 安装 + +依赖环境介绍 + +- gcc gcc-c++ + +> gcc为GNU Compiler Collection的缩写,可以编译C和C++源代码等,它是GNU开发的C和C++以及其他很多种语言 的编译器(最早的时候只能编译C,后来很快进化成一个编译多种语言的集合,如Fortran、Pascal、Objective-C、Java、Ada、 Go等。) +gcc 在编译C++源代码的阶段,只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库链接(编译过程分为编译、链接两个阶段,注意不要和可执行文件这个概念搞混,相对可执行文件来说有三个重要的概念:编译(compile)、链接(link)、加载(load)。源程序文件被编译成目标文件,多个目标文件连同库被链接成一个最终的可执行文件,可执行文件被加载到内存中运行)。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。 +gcc-c++也能编译C源代码,只不过把会把它当成C++源代码,后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的。 +- make automake + +> make是一个用来控制可执行文件和其他一些从源文件来的非源代码文件版本的软件。Make可以从一个名为makefile的文件中获得如何构建你所写程序的依赖关系,Makefile中列出了每个目标文件以及如何由其他文件来生成它。 +`automake`是一个从`Makefile.am`文件自动生成`Makefile.in`的工具。为了生成`Makefile.in`,automake还需用到perl,由于`automake`创建的发布完全遵循GNU标准,所以在创建中不需要`perl`。libtool是一款方便生成各种程序库的工具。 + +- autoconf + +> autoconf是用来生成自动配置软件源代码脚本(configure)的工具 + +- pcre pcre-devel + +> 在Nginx编译需要 PCRE(Perl Compatible Regular Expression),因为Nginx 的Rewrite模块和HTTP 核心模块会使用到PCRE正则表达式语法。 + +- zlip zlib-devel + +> nginx启用压缩功能的时候,需要此模块的支持。 +- openssl openssl-devel + +> 开启SSL的时候需要此模块的支持。 +- libtool + +> libtool是一个通用库支持脚本,将使用动态库的复杂性隐藏在统一、可移植的接口中;使用libtool的标准方法,可以在不同平台上创建并调用动态库。 +libtool主要的一个作用是在编译大型软件的过程中解决了库的依赖问题;将繁重的库依赖关系的维护工作承担下来,从而释放了程序员的人力资源。libtool提供统一的接口,隐藏了不同平台间库的名称的差异等细节,生成一个抽象的后缀名为la高层库`libxx.la`(其实是个文本文件),并将该库对其它库的依赖关系,都写在该la的文件中。 + +```shell + +$ sudo yum -y install gcc gcc-c++ make automake autoconf pcre pcre-devel zlib zlib-devel openssl openssl-devel libtool +$ wget http://nginx.org/download/nginx-1.14.0.tar.gz +$ tar zxvf nginx-1.14.0.tar.gz +$ ./configure --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre +$ make && make install +``` + +### nginx 配置 + +nginx的关于web服务器的配置,在http项中.每个server 对应一个主机 + +``` +http { + server {} + server{} +} +``` + + +``` +user www-data; +pid /run/nginx.pid; +worker_processes auto; +worker_rlimit_nofile 65535; + +events { + multi_accept on; + worker_connections 65535; +} + +http { + charset utf-8; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + server_tokens off; + log_not_found off; + types_hash_max_size 2048; + client_max_body_size 16M; + + # MIME + include mime.types; + default_type application/octet-stream; + + # logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + # SSL + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; + + # Diffie-Hellman parameter for DHE ciphersuites + ssl_dhparam /etc/nginx/dhparam.pem; + + # Mozilla Intermediate configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + + # OCSP Stapling + ssl_stapling on; + ssl_stapling_verify on; + resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s; + resolver_timeout 2s; + #负载均衡 + upstream backend{ + server 127.0.0.1:8050 weight=1 max_fails=2 fail_timeout=10 ; + server 127.0.0.1:8060 weight=2 max_fails=2 fail_timeout=10 ; + } + + + # gzip + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + # HTTP 301 重定向 + server { + listen 80; + listen [::]:80; + server_name .example.com; + location / { + return 301 https://www.example.com$request_uri; + } + } + + server { + listen 80;#监听端口 + server_name example.com;#绑定域名 + root /var/www/example.com/public;#网站根目录 + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + # index.html fallback + location / { + try_files $uri $uri/ /index.html; + } + # favicon.ico + location = /favicon.ico { + log_not_found off; + access_log off; + } + + # robots.txt + location = /robots.txt { + log_not_found off; + access_log off; + } + + # assets, media + location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ { + expires 7d; + access_log off; + } + + # svg, fonts + location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ { + add_header Access-Control-Allow-Origin "*"; + expires 7d; + access_log off; + } + # php配置 + location ~ \.php$ { + include fastcgi_params; + # fastcgi settings + # fastcgi_pass 127.0.0.1:9000 + fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; + fastcgi_index index.php; + fastcgi_buffers 8 16k; + fastcgi_buffer_size 32k; + # fastcgi params + fastcgi_param DOCUMENT_ROOT $realpath_root; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param PHP_ADMIN_VALUE "open_basedir=$base/:/usr/lib/php/:/tmp/"; + } + #反向代理 + location ^~/api/ { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } + # 负载 + location ^~/user/ { + proxy_set_header X-Real-IP $remote_addr; + proxy_pass http://backend + } + } + +} +``` + +### nginx 负载均衡的方式 + +- 权重 +``` +upstream backend{ + server 10.0.0.77 weight=5; + server 10.0.0.88 weight=10; +} +``` +- ip_hash + +根据客户端的ip的hash结果进行分配。这样每一个访客的固定访问的服务器都是一台机器 +``` +upstream backend{ + server 10.0.0.77; + server 10.0.0.88; +} +``` +- fair 第三方 + +按后端服务器的响应时间来分配请求。响应时间短的优先分配。 + +``` + upstream backend{ + server 10.0.0.10:8080; + server 10.0.0.11:8080; + fair; +} +``` +- url_hash +按訪问url的hash结果来分配请求,使每一个url定向到同一个后端服务器。后端服务器为缓存时比較有效。 + +``` + upstream backend{ + server 10.0.0.10:7777; + server 10.0.0.11:8888; + hash $request_uri; + hash_method crc32; +} + +upstream bakend{ + #定义负载均衡设备的Ip及设备状态 + ip_hash; + server 10.0.0.11:9090 down; + server 10.0.0.11:8080 weight=2; + server 10.0.0.11:6060; + server 10.0.0.11:7070 backup; +} +``` + +upstream还能够为每一个设备设置状态值,这些状态值的含义分别例如以下: + +- down 表示单前的server临时不參与负载. + +- weight 默觉得1.weight越大,负载的权重就越大。 + +- max_fails :同意请求失败的次数默觉得1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误. + +- fail_timeout : max_fails次失败后。暂停的时间。 + +- backup: 其他全部的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。 + +### nginx启动 和停止停止 +```shell +$ /usr/local/nginx/sbin/nginx + +#平滑启动 +$ /usr/local/nginx/sbin/nginx -s reload +#停止 +$ /usr/local/nginx/sbin/nginx -s stop +``` \ No newline at end of file diff --git a/Linux/README.md b/Linux/README.md index 2c3faeb..b06ebf7 100644 --- a/Linux/README.md +++ b/Linux/README.md @@ -6,7 +6,36 @@ ![img](https://images2015.cnblogs.com/blog/1066162/201611/1066162-20161130112310443-1027054412.png) -## Linux和Unix +### 内核态和用户态 + +操作系统为了管理内存。将内存分为**内核空间**(内核态)和**用户空间**。内存空间和用户空间之间有隔离。程序需要访问系统资源必须向内核空间进行申请。由内核把数据读取到用户空间。 + +Linux操作系统中主要采用了0和3两个特权级,分别对应的就是内核态和用户态。运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。很多程序开始时运行于用户态,但在执行的过程中,一些操作需要在内核权限下才能执行,这就涉及到一个从用户态切换到内核态的过程 + +应用程序访问内核,一般有两种调用方式:系统调用和库函数调用 + +**系统调用**:应用程序直接调用操作系统提供的接口 如write 函数 + +**库函数调用**:应用程序通过一些库函数直接调用 如 fwrite + +系统调用(英语:system call),指运行在用户空间的应用程序向操作系统内核请求某些服务的调用过程。 系统调用提供了用户程序与操作系统之间的接口。一般来说,系统调用都在内核态执行。由于系统调用不考虑平台差异性,由内核直接提供,因而移植性较差(几乎无移植性)。 + +库函数(library function),是由用户或组织自己开发的,具有一定功能的函数集合,一般具有较好平台移植性,通过库文件(静态库或动态库)向程序员提供功能性调用。程序员无需关心平台差异,由库来屏蔽平台差异性。 + +| 函数库调用 | 系统调用 | +| ----------------------------- | ----------------------- | +| 平台移植性好 | 依赖于内核,不保证移植性 | +| 调用函数库中的一段程序(或函数) | 调用系统内核的服务 | +| 一个普通功能函数的调用 | 是操作系统的一个入口点 | +| 在**用户空间**执行 | 在**内核空间**执行 | +| 它的运行时间属于“用户时间” | 它的运行时间属于“系统”时间 | +| 属于过程调用,调用开销较小 | 在用户空间和内核上下文环境间切换,开销较大 | +| 库函数数量较多 | UNIX中大约有90个系统调用,较少 | +| 典型的C函数库调用:printf scanf malloc | 典型的系统调用:fork open write | + +**用户空间即上层应用程序的活动空间**,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用 + +## Linux和Unix Unix系统于1969年在AT&T的贝尔实验室诞生,20世纪70年代,它逐步盛行,这期间,又产生了一个比较重要的分支,就是大约1977年诞生的BSD(Berkeley Software Distribution)系统。从BSD系统开始,各大厂商及商业公司开始了根据自身公司的硬件架构,并以BSD系统为基础进行Unix系统的研发,从而产生了各种版本的Unix系统. diff --git a/Linux/Sed.md b/Linux/Sed.md new file mode 100644 index 0000000..f4b770d --- /dev/null +++ b/Linux/Sed.md @@ -0,0 +1,118 @@ +## sed + +sed:Stream Editor文本流编辑,sed是一个“非交互式的”面向字符流的编辑器。能同时处理多个文件多行的内容,可以不对原文件改动,把整个文件输入到屏幕,可以把只匹配到模式的内容输入到屏幕上。还可以对原文件改动,但是不会再屏幕上返回结果。 + +**sed命令的语法格式:** + +sed的命令格式: `sed [option] 'sed command'filename` + +sed的脚本格式:`sed [option] -f 'sed script'filename` + +**sed命令的选项(option):** + +-n :只打印模式匹配的行 + +-e :直接在命令行模式上进行sed动作编辑,此为默认选项 + +-f :将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作 + +-r :支持扩展表达式 + +-i :直接修改文件内容 + +**sed在文件中查询文本的方式:** + +**1)使用行号,可以是一个简单数字,或是一个行号范围** + +| x | x为行号 | +| ----------------- | ---------------------------- | +| x,y | 表示行号从x到y | +| /pattern | 查询包含模式的行 | +| /pattern /pattern | 查询包含两个模式的行 | +| pattern/,x | 在给定行号上查询包含模式的行 | +| x,/pattern/ | 通过行号和模式查询匹配的行 | +| x,y! | 查询不包含指定行号x和y的行 | + +**sed的编辑命令(sed command):** + +| p | 打印匹配行(和-n选项一起合用) | +| ---------- | ------------------------------------------------------------ | +| = | 显示文件行号 | +| a\ | 在定位行号后附加新文本信息 | +| i\ | 在定位行号后插入新文本信息 | +| d | 删除定位行 | +| c\ | 用新文本替换定位文本 | +| w filename | 写文本到一个文件,类似输出重定向 > | +| r filename | 从另一个文件中读文本,类似输入重定向 < | +| s | 使用替换模式替换相应模式 | +| q | 第一个模式匹配完成后退出或立即退出 | +| l | 显示与八进制ACSII代码等价的控制符 | +| {} | 在定位行执行的命令组,用分号隔开 | +| n | 从另一个文件中读文本下一行,并从下一条命令而不是第一条命令开始对其的处理 | +| N | 在数据流中添加下一行以创建用于处理的多行组 | +| g | 将模式2粘贴到/pattern n/ | +| y | 传送字符,替换单个字符 | + +| 操作符 | 名字 | 效果 | +| --------------------------------- | --------- | ------------------------------------------------------------ | +| `[地址范围]/p` | 打印 | 打印[指定的地址范围] `3,5/p` | +| `[地址范围]/d` | 删除 | 删除[指定的地址范围] | +| `s/pattern1/pattern2/` | 替换 | 将指定行中, 将第一个匹配到的pattern1, 替换为pattern2. | +| `[地址范围]/s/pattern1/pattern2/` | 替换 | 在`*地址范围*`指定的每一行中, 将第一个匹配到的pattern1, 替换为pattern2. | +| `[地址范围]/y/pattern1/pattern2/` | transform | 在`*地址范围*`指定的每一行中, 将pattern1中的每个匹配到pattern2的字符都使用pattern2的相应字符作替换. (等价于tr命令) | +| `g` | 全局 | 在每个匹配的输入行中, 将*每个*模式匹配都作相应的操作. (译者注: 不只局限于第一个匹配) | + +- 过滤PHP.ini中空行和注释 + +```shell +sed -n '/^;/!p{/^$/!p}' php.ini +``` + +- 打印指定行数的内容 + +```shell +sed -n '3,6'p php.ini +``` + +- 打印匹配行 + +```shell +sed -n '/php/p' php.ini +``` + +### 替换 s + +```shell +sed -n 's/php/PHP/g' php.ini #把php 替换成PHP +``` + +### 追加 a\ + +对源文件追加 加-i + +```shell +sed '/^test/a\this is a test line' file #将 this is a test line 追加到 以test 开头的行后面: +sed '/^test/i\this is begin/' #将this is end 追加到匹配的行头 +``` + +- 行尾追加字符 + +```shell +sed '/php/s/$/ PHP/' php.ini +``` + +- 行首追加 + +```shell +sed 's/^/START/' +``` + +### 删除 d + +```shell +sed '/^$/d' file #删除空白 +sed '1,10d' file # 删除1-10行 +sed '/^$/' file #删除空白行 +sed '/^PHP/d' file #删除PHP开头的行 +``` + diff --git a/Linux/Vim.md b/Linux/Vim.md new file mode 100644 index 0000000..066579a --- /dev/null +++ b/Linux/Vim.md @@ -0,0 +1,96 @@ +## vim + +vim是一个类似vi编辑器的。有一个段子:程序员分为三类,一种是用`vim`的 一种是用`emacs` ,剩下的一种是用其他编辑器的。可见vim的流传度。vim的设计理解,是命令的组合。就是完全不用鼠标。通过命令就可以。比如我们在其他编辑器。如果跑到多少行,我们可能需要滚动鼠标。但是vim就在命令行模式下就完成了。先上一个图 + +![img](../MQ/images/vi-vim-cheat-sheet-sch1.gif) + +### VIM的模式 + +vim的模式有三种:分别为命令模式,输入模式、尾行模式 + +![img](../MQ/images/vim-vi-workmodel.png) + +#### 1. 命令模式 + +默认用vim打开一个文件的时候,就是进入了命令模式。在这个模式下。可以通过各种命令组合操作文本编辑器 + +#### 2. 输入模式 + +输入模式,就是和我们正常的编辑器一样。可以在这个模式下。编辑修改打开文件的内容 + +在命令模式下。按`i`键。就是输入模式。按`ESC` 退出输入模式,进入到命令模式 + +#### 3. 尾行模式 + +在命令模式下`:` 进入尾行。尾行模式下命令也非常多。主要包括文件的查找,保存等 + +### VIM命令模式下的快捷键 + +介绍一些常用的快捷键。 + +上下左右键可能跟我们之前的不一样。一般游戏爱好者的上下左右是wsad。但是在vim就是hjkl。 + +| 移动光标的方法 | | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| h 或 向左箭头键(←) | 光标向左移动一个字符 | +| j 或 向下箭头键(↓) | 光标向下移动一个字符 | +| k 或 向上箭头键(↑) | 光标向上移动一个字符 | +| l 或 向右箭头键(→) | 光标向右移动一个字符 | +| 如果你将右手放在键盘上的话,你会发现 hjkl 是排列在一起的,因此可以使用这四个按钮来移动光标。 如果想要进行多次移动的话,例如向下移动 30 行,可以使用 "30j" 或 "30↓" 的组合按键, 亦即加上想要进行的次数(数字)后,按下动作即可! | | +| [Ctrl] + [f] | 屏幕『向下』移动一页,相当于 [Page Down]按键 (常用) | +| [Ctrl] + [b] | 屏幕『向上』移动一页,相当于 [Page Up] 按键 (常用) | +| [Ctrl] + [d] | 屏幕『向下』移动半页 | +| [Ctrl] + [u] | 屏幕『向上』移动半页 | +| + | 光标移动到非空格符的下一行 | +| - | 光标移动到非空格符的上一行 | +| | | +| 0 或功能键[Home] | 这是数字『 0 』:移动到这一行的最前面字符处 (常用) | +| $ 或功能键[End] | 移动到这一行的最后面字符处(常用) | +| H | 光标移动到这个屏幕的最上方那一行的第一个字符 | +| M | 光标移动到这个屏幕的中央那一行的第一个字符 | +| L | 光标移动到这个屏幕的最下方那一行的第一个字符 | +| G | 移动到这个档案的最后一行(常用) | +| nG | n 为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行(可配合 :set nu) | +| gg | 移动到这个档案的第一行,相当于 1G 啊! (常用) | +| x, X | 在一行字当中,x 为向后删除一个字符 (相当于 [del] 按键), X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) (常用) | +| -------- | ------------------------------------------------------------ | +| nx | n 为数字,连续向后删除 n 个字符。举例来说,我要连续删除 10 个字符, 『10x』。 | +| **dd** | 删除游标所在的那一整行(常用) | +| **ndd** | n 为数字。删除光标所在的向下 n 行,例如 20dd 则是删除 20 行 (常用) | +| d1G | 删除光标所在到第一行的所有数据 | +| dG | 删除光标所在到最后一行的所有数据 | +| d$ | 删除游标所在处,到该行的最后一个字符 | +| d0 | 那个是数字的 0 ,删除游标所在处,到该行的最前面一个字符 | +| **yy** | 复制游标所在的那一行(常用) | +| **nyy** | n 为数字。复制光标所在的向下 n 行,例如 20yy 则是复制 20 行(常用) | +| y1G | 复制游标所在行到第一行的所有数据 | +| yG | 复制游标所在行到最后一行的所有数据 | +| y0 | 复制光标所在的那个字符到该行行首的所有数据 | +| y$ | 复制光标所在的那个字符到该行行尾的所有数据 | +| **p, P** | p 为将已复制的数据在光标下一行贴上,P 则为贴在游标上一行! 举例来说,我目前光标在第 20 行,且已经复制了 10 行数据。则按下 p 后, 那 10 行数据会贴在原本的 20 行之后,亦即由 21 行开始贴。但如果是按下 P 呢? 那么原本的第 20 行会被推到变成 30 行。 (常用) | +| J | 将光标所在行与下一行的数据结合成同一行 | +| c | 重复删除多个数据,例如向下删除 10 行,[ 10cj ] | +| u | 复原前一个动作。(常用) | +| [Ctrl]+r | 重做上一个动作。(常用) | + +### 进入编辑模式的命令 + +- `i` i 为『从目前光标所在处输入』 insert +- `I` I 为『在目前所在行的第一个非空格符处开始输入』 +- `a` a 为『从目前光标所在的下一个字符处开始输入』 append +- A 为『从光标所在行的最后一个字符处开始输入』。(常用) +- `ESC` 退回到命令模式 + + + +### 尾行模式下的命令 + +- `:w` 将编辑的数据写入硬盘档案中 +- `:w!`若文件属性为『只读』时,强制写入该档案 +- `:q` 退出文件 +- `:q!` 不保存退出 +- `:wq ` 保存退出 +- `:wq!` 强制保存退出 + + + diff --git a/Linux/crontab.md b/Linux/crontab.md index b79a303..e0f7273 100644 --- a/Linux/crontab.md +++ b/Linux/crontab.md @@ -19,7 +19,7 @@ ```shell * * * * * echo "hello" #每1分钟执行hello -3,15 * * * * myCommand #每小时第三分钟和第五分钟执行 +3,15 * * * * myCommand #每小时第三分钟和第十五分钟执行 3,15 8-11 * * * myCommand# 在上午8点到11点的第3和第15分钟执行 3,15 8-11 */2 * * myCommand #每隔两天的上午8点到11点的第3和第15分钟执行 30 21 * * * /etc/init.d/smb restart #每晚的21:30重启smb @@ -34,4 +34,4 @@ ``` $service cron restart -``` \ No newline at end of file +``` diff --git a/Linux/lanmp.md b/Linux/lanmp.md index 0d3a928..e8e2528 100644 --- a/Linux/lanmp.md +++ b/Linux/lanmp.md @@ -119,13 +119,4 @@ AddType application/x-httpd-php .php ```SHELL service httpd restart -``` - - - -### Lnmp安装 - - - - - +``` \ No newline at end of file diff --git a/Linux/shell.md b/Linux/shell.md index 252ee07..e91873f 100644 --- a/Linux/shell.md +++ b/Linux/shell.md @@ -7,7 +7,7 @@ - 头声明 -shell脚本第一行必须以 #!开头,它表示该脚本使用后面的解释器解释执行。 +shell脚本第一行必须以 #!开头,它表示该脚本使用后面的解释器解释执行。 ```shell #!/bin/bash diff --git "a/Linux/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/Linux/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" new file mode 100644 index 0000000..7d4777d --- /dev/null +++ "b/Linux/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" @@ -0,0 +1,9 @@ +## 进程和线程的区别 + +进程是一次程序运行的活动。进程有自己的pid,堆栈空间等资源。 + +线程是进程里面的一个实体。是CPU调度的基本单位。它比进程更小。线程本身不拥有系统资源。只拥有自己的程序计数器、堆栈、寄存器。和同一个进程中的其他线程共享进程中的内存。 + +线程开销小。进程切换开销比较大。进程切换,上下文。 + +进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。 \ No newline at end of file diff --git a/MQ/README.md b/MQ/README.md new file mode 100644 index 0000000..d762ed6 --- /dev/null +++ b/MQ/README.md @@ -0,0 +1,186 @@ +## 消息队列(Message Queue) + +“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂 ,包括对象等。 + +队列是一种数据结构,先进先出,保证了顺序性。 + +生产者:发送消息的一端。用于把消息写入到队列中 + +消费者:从消息队列中,依次读取每条消息的一端。 + +消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。 + +目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。 + +### 应用场景 + +#### 1. 异步处理 + +场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式 + +(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端 + + ![img](images/820332-20160124211106000-2080222350.png) + +(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间 + + ![img](images/820332-20160124211115703-218873208.png) + +假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。 + +因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100) + + + +引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下: + + ![img](images/820332-20160124211131625-1083908699.png) + +按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍 + +#### 2. 应用解耦 + +场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图: + +![img](http://static.codeceo.com/images/2016/02/4a63d31c026678ec4f5e4c65615524a5.png) + +传统模式的缺点: + +1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败; + +2) 订单系统与库存系统耦合; + +如何解决以上问题呢?引入应用消息队列后的方案,如下图 + +![img](images/a0d2f7ae4bc26ac1b0534660b51af7b9.png) + +- 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。 +- 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。 +- 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。 + +#### 3. 流量削峰 + +流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。 + +应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。 + +1. 可以控制活动的人数; +2. 可以缓解短时间内高流量压垮应用; + +![img](images/addea89be214fa66a0d45db711da4f91.png) + +1. 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面; +2. 秒杀业务根据消息队列中的请求信息,再做后续处理。 + +#### 4. 日志处理 + +日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下: + +![img](images/91a956e890623d5f05b3dac013d8dd3a.png) + +- 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列; +- Kafka消息队列,负责日志数据的接收,存储和转发; +- 日志处理应用:订阅并消费kafka队列中的日志数据; + +#### 5. 消息通讯 + +消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。 + +点对点通讯: + +![img](images/f88e45bdce945fe93c31d68df4059146.png) + +客户端A和客户端B使用同一队列,进行消息通讯。 + +聊天室通讯: + +![img](images/61c6fd8e58722d438da19445c8016395.png) + +客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。 + +以上实际是消息队列的两种消息模式,点对点或发布订阅模式。 + +### 消息中间件实例 + +#### 电商系统 + + ![img](images/820332-20160124220821515-1142658553.jpg) + +消息队列采用高可用,可持久化的消息中间件。比如Active MQ,Rabbit MQ,Rocket Mq。 + +(1)应用将主干逻辑处理完成后,写入消息队列。消息发送是否成功可以开启消息的确认模式。(消息队列返回消息接收成功状态后,应用再返回,这样保障消息的完整性) + +(2)扩展流程(发短信,配送处理)订阅队列消息。采用推或拉的方式获取消息并处理。 + +(3)消息将应用解耦的同时,带来了数据一致性问题,可以采用最终一致性方式解决。比如主数据写入数据库,扩展应用根据消息队列,并结合数据库方式实现基于消息队列的后续处理。 + +#### 日志收集系统 + + ![img](images/820332-20160124220830750-1886187340.jpg) + +分为Zookeeper注册中心,日志收集客户端,Kafka集群和Storm集群(OtherApp)四部分组成。 + +- Zookeeper注册中心,提出负载均衡和地址查找服务 +- 日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列 +- Kafka集群:接收,路由,存储,转发等消息处理 + +Storm集群:与OtherApp处于同一级别,采用拉的方式消费队列中的数据 + +### 消息模型 + +JMS标准中,有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。 + +#### P2P(点对点)模式 + +![img](images/20151201162724900.jpg) + +P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。 + +P2P的特点 + +- 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中) +- 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列 +- 接收者在成功接收消息之后需向队列应答成功 + +如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式 + +#### Pub/sub模式 + +消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。 + +![img](images/20151201162752176.jpg) + +包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。 + +Pub/Sub的特点 + +- 每个消息可以有多个消费者 +- 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息 +- 为了消费消息,订阅者必须保持运行的状态 + +为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。 + +如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型 + +### 流行模型对比 + +传统企业型消息队列ActiveMQ遵循了JMS规范,实现了点对点和发布订阅模型,但其他流行的消息队列RabbitMQ、Kafka并没有遵循JMS规范。 + +#### RabbitMQ + +RabbitMQ实现了AQMP协议,AQMP协议定义了消息路由规则和方式。生产端通过路由规则发送消息到不同queue,消费端根据queue名称消费消息。 RabbitMQ既支持内存队列也支持持久化队列,消费端为推模型,消费状态和订阅关系由服务端负责维护,消息消费完后立即删除,不保留历史消息。 + +(1)点对点 +生产端发送一条消息通过路由投递到Queue,只有一个消费者能消费到。 + +![img](images/20151201162841986.jpg) +(2)多订阅 +当RabbitMQ需要支持多订阅时,发布者发送的消息通过路由同时写到多个Queue,不同订阅组消费不同的Queue。所以支持多订阅时,消息会多个拷贝。 + +![img](images/20151201162903057.jpg) + +#### Kafka + +Kafka只支持**消息持久化**,消费端为拉模型,消费状态和订阅关系由客户端端负责维护,消息消费完后不会立即删除,会保留历史消息。因此支持多订阅时,消息只会存储一份就可以了。但是可能产生重复消费的情况。 + +(1)点对点&多订阅 发布者生产一条消息到topic中,不同订阅组消费此消息。 ![img](images/20151201162920224.jpg) \ No newline at end of file diff --git a/MQ/images/20140220173559828 b/MQ/images/20140220173559828 new file mode 100644 index 0000000..8e4ae9a Binary files /dev/null and b/MQ/images/20140220173559828 differ diff --git a/MQ/images/20151201162724900.jpg b/MQ/images/20151201162724900.jpg new file mode 100644 index 0000000..606b03e Binary files /dev/null and b/MQ/images/20151201162724900.jpg differ diff --git a/MQ/images/20151201162752176.jpg b/MQ/images/20151201162752176.jpg new file mode 100644 index 0000000..5da060a Binary files /dev/null and b/MQ/images/20151201162752176.jpg differ diff --git a/MQ/images/20151201162825775.jpg b/MQ/images/20151201162825775.jpg new file mode 100644 index 0000000..32064a5 Binary files /dev/null and b/MQ/images/20151201162825775.jpg differ diff --git a/MQ/images/20151201162841986.jpg b/MQ/images/20151201162841986.jpg new file mode 100644 index 0000000..2247d53 Binary files /dev/null and b/MQ/images/20151201162841986.jpg differ diff --git a/MQ/images/20151201162903057.jpg b/MQ/images/20151201162903057.jpg new file mode 100644 index 0000000..cb106f2 Binary files /dev/null and b/MQ/images/20151201162903057.jpg differ diff --git a/MQ/images/20151201162920224.jpg b/MQ/images/20151201162920224.jpg new file mode 100644 index 0000000..e6c2563 Binary files /dev/null and b/MQ/images/20151201162920224.jpg differ diff --git a/MQ/images/20160516173232943 b/MQ/images/20160516173232943 new file mode 100644 index 0000000..81b11b1 Binary files /dev/null and b/MQ/images/20160516173232943 differ diff --git a/MQ/images/20160516173308130 b/MQ/images/20160516173308130 new file mode 100644 index 0000000..f0cc6c9 Binary files /dev/null and b/MQ/images/20160516173308130 differ diff --git a/MQ/images/20161018130024488 b/MQ/images/20161018130024488 new file mode 100644 index 0000000..9e8e26a Binary files /dev/null and b/MQ/images/20161018130024488 differ diff --git a/MQ/images/20161018144117548 b/MQ/images/20161018144117548 new file mode 100644 index 0000000..46ce129 Binary files /dev/null and b/MQ/images/20161018144117548 differ diff --git a/MQ/images/20161103174653291.jpg b/MQ/images/20161103174653291.jpg new file mode 100644 index 0000000..5fd31e6 Binary files /dev/null and b/MQ/images/20161103174653291.jpg differ diff --git a/MQ/images/20161103182929513.jpg b/MQ/images/20161103182929513.jpg new file mode 100644 index 0000000..1f98cae Binary files /dev/null and b/MQ/images/20161103182929513.jpg differ diff --git a/MQ/images/20161103182938987.jpg b/MQ/images/20161103182938987.jpg new file mode 100644 index 0000000..f005bfe Binary files /dev/null and b/MQ/images/20161103182938987.jpg differ diff --git a/MQ/images/306976-20160728104237622-1486261669.png b/MQ/images/306976-20160728104237622-1486261669.png new file mode 100644 index 0000000..8535ce0 Binary files /dev/null and b/MQ/images/306976-20160728104237622-1486261669.png differ diff --git a/MQ/images/306976-20160728104255372-2049742072.png b/MQ/images/306976-20160728104255372-2049742072.png new file mode 100644 index 0000000..77873f7 Binary files /dev/null and b/MQ/images/306976-20160728104255372-2049742072.png differ diff --git a/MQ/images/306976-20160728104309934-1385658660.png b/MQ/images/306976-20160728104309934-1385658660.png new file mode 100644 index 0000000..13bb277 Binary files /dev/null and b/MQ/images/306976-20160728104309934-1385658660.png differ diff --git a/MQ/images/52im_1.png b/MQ/images/52im_1.png new file mode 100644 index 0000000..e7b1883 Binary files /dev/null and b/MQ/images/52im_1.png differ diff --git a/MQ/images/61c6fd8e58722d438da19445c8016395.png b/MQ/images/61c6fd8e58722d438da19445c8016395.png new file mode 100644 index 0000000..328daf4 Binary files /dev/null and b/MQ/images/61c6fd8e58722d438da19445c8016395.png differ diff --git a/MQ/images/820332-20160124211106000-2080222350.png b/MQ/images/820332-20160124211106000-2080222350.png new file mode 100644 index 0000000..020ca0a Binary files /dev/null and b/MQ/images/820332-20160124211106000-2080222350.png differ diff --git a/MQ/images/820332-20160124211115703-218873208.png b/MQ/images/820332-20160124211115703-218873208.png new file mode 100644 index 0000000..4a7cbec Binary files /dev/null and b/MQ/images/820332-20160124211115703-218873208.png differ diff --git a/MQ/images/820332-20160124211131625-1083908699.png b/MQ/images/820332-20160124211131625-1083908699.png new file mode 100644 index 0000000..585a2eb Binary files /dev/null and b/MQ/images/820332-20160124211131625-1083908699.png differ diff --git a/MQ/images/820332-20160124220821515-1142658553.jpg b/MQ/images/820332-20160124220821515-1142658553.jpg new file mode 100644 index 0000000..7b72072 Binary files /dev/null and b/MQ/images/820332-20160124220821515-1142658553.jpg differ diff --git a/MQ/images/820332-20160124220830750-1886187340.jpg b/MQ/images/820332-20160124220830750-1886187340.jpg new file mode 100644 index 0000000..81bff7a Binary files /dev/null and b/MQ/images/820332-20160124220830750-1886187340.jpg differ diff --git a/MQ/images/91a956e890623d5f05b3dac013d8dd3a.png b/MQ/images/91a956e890623d5f05b3dac013d8dd3a.png new file mode 100644 index 0000000..35b23fe Binary files /dev/null and b/MQ/images/91a956e890623d5f05b3dac013d8dd3a.png differ diff --git a/MQ/images/Decorator.jpg b/MQ/images/Decorator.jpg new file mode 100644 index 0000000..77cc2e7 Binary files /dev/null and b/MQ/images/Decorator.jpg differ diff --git a/MQ/images/Pic90.gif b/MQ/images/Pic90.gif new file mode 100644 index 0000000..7676751 Binary files /dev/null and b/MQ/images/Pic90.gif differ diff --git a/MQ/images/a0d2f7ae4bc26ac1b0534660b51af7b9.png b/MQ/images/a0d2f7ae4bc26ac1b0534660b51af7b9.png new file mode 100644 index 0000000..d34b379 Binary files /dev/null and b/MQ/images/a0d2f7ae4bc26ac1b0534660b51af7b9.png differ diff --git a/MQ/images/addea89be214fa66a0d45db711da4f91.png b/MQ/images/addea89be214fa66a0d45db711da4f91.png new file mode 100644 index 0000000..413cbf8 Binary files /dev/null and b/MQ/images/addea89be214fa66a0d45db711da4f91.png differ diff --git a/MQ/images/f88e45bdce945fe93c31d68df4059146.png b/MQ/images/f88e45bdce945fe93c31d68df4059146.png new file mode 100644 index 0000000..28e3e6f Binary files /dev/null and b/MQ/images/f88e45bdce945fe93c31d68df4059146.png differ diff --git a/MQ/images/uml6.png b/MQ/images/uml6.png new file mode 100644 index 0000000..3eff464 Binary files /dev/null and b/MQ/images/uml6.png differ diff --git a/MQ/images/v63YbyA.png b/MQ/images/v63YbyA.png new file mode 100644 index 0000000..ef17504 Binary files /dev/null and b/MQ/images/v63YbyA.png differ diff --git a/MQ/images/vi-vim-cheat-sheet-sch1.gif b/MQ/images/vi-vim-cheat-sheet-sch1.gif new file mode 100644 index 0000000..7caed81 Binary files /dev/null and b/MQ/images/vi-vim-cheat-sheet-sch1.gif differ diff --git a/MQ/images/vim-vi-workmodel.png b/MQ/images/vim-vi-workmodel.png new file mode 100644 index 0000000..3db4f5f Binary files /dev/null and b/MQ/images/vim-vi-workmodel.png differ diff --git a/MQ/question.md b/MQ/question.md new file mode 100644 index 0000000..f9ed9ec --- /dev/null +++ b/MQ/question.md @@ -0,0 +1,25 @@ +1. 消息队列的作用 +- 流量消峰 + 并发量大的时间,所有的请求直接怼到数据库,造成数据库连接异常,将请求写进消息队列,后面的系统再从消息队列依次来取出。 +- 异步 + 一些非必要的业务逻辑以同步的方式运行,太耗费时间。改成异步,可以提高系统的响应时间。 +- 解耦 + 将消息写入消息队列,需要消息的系统自己从消息队列中订阅。从而使该系统不需要改代码。 +2. 如何保证消息队列高可用 +集群 +3. 如何保证消息不被重复消费 +那造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。 +消费前做检测,比如写库成功的时候,写入到redis中,再次消费的时候如果redis已存在,则不进行消费 +4. 如何保证消费的可靠性传输? +其实这个可靠性传输,每种MQ都要从三个角度来分析: + + - 生产者弄丢数据 + + 从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息 + - 消息队列弄丢数据 + + 处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用 + - 消费者弄丢数据 + + 消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rabbitMQ会立即将消息删除,这种情况下,如果消费者出现异常而未能处理消息,就会丢失该消息。 + 手动确认消息 diff --git a/MQ/rabbitmq.md b/MQ/rabbitmq.md new file mode 100644 index 0000000..25044d3 --- /dev/null +++ b/MQ/rabbitmq.md @@ -0,0 +1,92 @@ +## RabbitMQ 消息队列 + +> RabbitMQ是流行的开源消息队列系统,用erlang语言开发,完整的实现了AMPQ(高级消息队列协议) + +## AMQP协议 + +> AMQP,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,同样,消息使用者也不用知道发送者的存在。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全 + +## 系统架构 + + + +![](images/20140220173559828) +消息队列的使用过程大概如下: + +(1)客户端连接到消息队列服务器,打开一个channel。 +(2)客户端声明一个exchange,并设置相关属性。 +(3)客户端声明一个queue,并设置相关属性。 +(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。 +(5)客户端投递消息到exchange。exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里 + +## Rabbitmq中几个概念的解释 + +- **生产者 ** + +![](images/20161103174653291.jpg) + +生产者就是产生消息并向RabbitMq队列发送消息 + +- **消费者** + +![](images/20161103182929513.jpg) + +等待RabbitMq消息到来并处理消息 + +- **Queue**(队列) + +![](images/20161103182938987.jpg) +Queue(队列), 依存于RabbitMQ内部,消息存在队列中。它指定消息按什么规则,路由到哪个队列 + +- **交换器(exchange)** + +![](http://ostest.qiniudn.com/wordpress/wp-content/uploads/2014/02/2014-2-21-9-51-03.png) + +生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个或多个Queue中.它指定消息按什么规则,路由到哪个队列 + +- **binding 绑定** + +![](http://ostest.qiniudn.com/wordpress/wp-content/uploads/2014/02/2014-2-21-9-52-46.png) + +它的作用就是把exchange和queue按照路由规则绑定起来 + +- routing key 路由关键字 + +exchange根据这个关键字将消息投放到对应的队列中去。 + +- **Binding key** + +在绑定(Binding)Exchange与Queue的同时,一般会指定一个binding key;生产者将消息发送给Exchange时,一般会指定一个routing key;当binding key与routing key相匹配时,消息将会被路由到对应的Queue中 + +- **虚拟主机** + +一个虚拟主机持有一组交换机、队列和绑定。隔离不同的队列和用户的权限管理。 + +![](images/v63YbyA.png) + +- **channel 消息通道** + +在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务 + +-Exchange type 交换模式 + +RabbitMQ提供了四种Exchange模式:fanout,direct,topic,header + +一、 **Fanout** + +![](images/306976-20160728104237622-1486261669.png) + +它采取广播模式,消息进来时,将会被投递到与改交换机绑定的所有队列中。 +所有发送到Fanout Exchange的消息都会被转发到与该Exchange 绑定(Binding)的所有Queue上.Fanout Exchange 不需要处理RouteKey 。只需要简单的将队列绑定到exchange + +二、**Direct ** + +![](images/306976-20160728104255372-2049742072.png) + +Direct模式,消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃。 + +三. **Topic ** + +![](images/306976-20160728104309934-1385658660.png) + +Exchange 将RouteKey 和某Topic 进行模糊匹配。此时队列需要绑定一个Topic。可以使用通配符进行模糊匹配,符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“log.#”能够匹配到“log.info.oa”,但是“log.*” 只会匹配到“log.error \ No newline at end of file diff --git a/MongoDb/MongoDB.md b/MongoDb/MongoDB.md index 55bfa48..1526327 100644 --- a/MongoDb/MongoDB.md +++ b/MongoDb/MongoDB.md @@ -235,15 +235,15 @@ db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}]) # select by_user, count(*) from mycol group by by_user ``` -| $sum | 计算总和。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) | +| `$sum` | 计算总和。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) | | --------- | ---------------------------------------------- | ------------------------------------------------------------ | -| $avg | 计算平均值 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) | -| $min | 获取集合中所有文档对应值得最小值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}]) | +| `$avg` | 计算平均值 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) | +| `$min` | 获取集合中所有文档对应值得最小值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}]) | | $max | 获取集合中所有文档对应值得最大值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}]) | -| $push | 在结果文档中插入值到一个数组中。 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}]) | +| `$push` | 在结果文档中插入值到一个数组中。 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}]) | | $addToSet | 在结果文档中插入值到一个数组中,但不创建副本。 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}]) | | $first | 根据资源文档的排序获取第一个文档数据。 | db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}]) | -| $last | 根据资源文档的排序获取最后一个文档数据 | db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}]) | +| `$last` | 根据资源文档的排序获取最后一个文档数据 | db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}]) | 聚合框架中常用的几个操作: @@ -264,6 +264,50 @@ db.articles.aggregate( [ #$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。 ``` +**聚合后排序操作** + +```bash +db.getCollection('position').aggregate({ + "$group": { + "_id": "$create_time", + "count": { + "$sum": 1 + } + } + +},{ + "$sort": { + "_id": -1 + } +}) +``` + +**起别名** + +```bash +db.getCollection('position').aggregate({ + "$group": { + "_id": "$create_time", + "count": { + "$sum": 1 + } + } + +},{ + "$sort": { + "_id": -1 + } + },{ + "$project": { + "date": "$_id", + "count": 1, + "_id": 0 + } + }) +``` + + + ### 原子性和事务处理 diff --git "a/Mysql/MySQL\344\274\230\345\214\226.md" "b/Mysql/MySQL\344\274\230\345\214\226.md" index bcc8aa4..4531e19 100644 --- "a/Mysql/MySQL\344\274\230\345\214\226.md" +++ "b/Mysql/MySQL\344\274\230\345\214\226.md" @@ -34,9 +34,8 @@ select * from user where name like '%a' - 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描 -- 很多时候用 exists 代替 in 是一个好的选择: +- 很多时候用 exists 代替 in 是一个好的选择 -- ## btree索引 diff --git "a/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" "b/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" index 9cb8a86..2fcb85e 100644 --- "a/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" +++ "b/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" @@ -4,14 +4,14 @@ MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为 ```sql select - count(*) + count(*) from - task + task where - status=2 - and operator_id=20839 - and operate_time>1371169729 - and operate_time<1371174603 + status=2 + and operator_id=20839 + and operate_time>1371169729 + and operate_time<1371174603 and type=2; ``` @@ -42,8 +42,10 @@ various-system-software-hardware-latencies ### 详解b+树 +![b+树](https://tech.meituan.com/img/mysql_index/btree.jpg) + b+树 -如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。 +如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。 ### b+树的查找过程 @@ -80,12 +82,12 @@ select count(*) from task where status = 0 ; ### 慢查询优化基本步骤 0. 先运行看看是否真的很慢,注意设置SQL_NO_CACHE -1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的 where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 -1. explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) -2. order by limit 形式的sql语句让排序的表优先查 -3. 了解业务方使用场景 -4. 加索引时参照建索引的几大原则 -5. 观察结果,不符合预期继续从0分析 +1. where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的 where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 +2. explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) +3. order by limit 形式的sql语句让排序的表优先查 +4. 了解业务方使用场景 +5. 加索引时参照建索引的几大原则 +6. 观察结果,不符合预期继续从0分析 几个慢查询案例 下面几个例子详细解释了如何分析和优化慢查询 @@ -95,32 +97,32 @@ select count(*) from task where status = 0 ; ```sql select - distinct cert.emp_id + distinct cert.emp_id from - cm_log cl + cm_log cl inner join ( select emp.id as emp_id, - emp_cert.id as cert_id + emp_cert.id as cert_id from - employee emp + employee emp left join - emp_certificate emp_cert - on emp.id = emp_cert.emp_id + emp_certificate emp_cert + on emp.id = emp_cert.emp_id where emp.is_deleted=0 - ) cert + ) cert on ( - cl.ref_table='Employee' + cl.ref_table='Employee' and cl.ref_oid= cert.emp_id - ) + ) or ( - cl.ref_table='EmpCertificate' + cl.ref_table='EmpCertificate' and cl.ref_oid= cert.cert_id - ) + ) where - cl.last_upd_date >='2013-11-07 15:03:00' + cl.last_upd_date >='2013-11-07 15:03:00' and cl.last_upd_date<='2013-11-08 16:00:00'; ``` @@ -129,8 +131,9 @@ where 先运行一下,53条记录 1.87秒,又没有用聚合语句,比较慢 53 rows in set (1.87 sec) -1.explain +1.explain +```sql +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ @@ -139,42 +142,43 @@ where | 2 | DERIVED | emp | ALL | NULL | NULL | NULL | NULL | 13317 | Using where | | 2 | DERIVED | emp_cert | ref | emp_certificate_empid | emp_certificate_empid | 4 | meituanorg.emp.id | 1 | Using index | +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ +``` 简述一下执行计划,首先mysql根据idx_last_upd_date索引扫描cm_log表获得379条记录;然后查表扫描了63727条记录,分为两部分,derived表示构造表,也就是不存在的表,可以简单理解成是一个语句形成的结果集,后面的数字表示语句的ID。derived2表示的是ID = 2的查询构造了虚拟表,并且返回了63727条记录。我们再来看看ID = 2的语句究竟做了写什么返回了这么大量的数据,首先全表扫描employee表13317条记录,然后根据索引emp_certificate_empid关联emp_certificate表,rows = 1表示,每个关联都只锁定了一条记录,效率比较高。获得后,再和cm_log的379条记录根据规则关联。从执行过程上可以看出返回了太多的数据,返回的数据绝大部分cm_log都用不到,因为cm_log只锁定了379条记录。 如何优化呢?可以看到我们在运行完后还是要和cm_log做join,那么我们能不能之前和cm_log做join呢?仔细分析语句不难发现,其基本思想是如果cm_log的ref_table是EmpCertificate就关联emp_certificate表,如果ref_table是Employee就关联employee表,我们完全可以拆成两部分,并用union连接起来,注意这里用union,而不用union all是因为原语句有“distinct”来得到唯一的记录,而union恰好具备了这种功能。如果原语句中没有distinct不需要去重,我们就可以直接使用union all了,因为使用union需要去重的动作,会影响SQL性能。 优化过的语句如下 ```sql select - emp.id + emp.id from - cm_log cl + cm_log cl inner join - employee emp - on cl.ref_table = 'Employee' - and cl.ref_oid = emp.id + employee emp + on cl.ref_table = 'Employee' + and cl.ref_oid = emp.id where - cl.last_upd_date >='2013-11-07 15:03:00' - and cl.last_upd_date<='2013-11-08 16:00:00' - and emp.is_deleted = 0 + cl.last_upd_date >='2013-11-07 15:03:00' + and cl.last_upd_date<='2013-11-08 16:00:00' + and emp.is_deleted = 0 union select - emp.id + emp.id from - cm_log cl + cm_log cl inner join - emp_certificate ec - on cl.ref_table = 'EmpCertificate' - and cl.ref_oid = ec.id + emp_certificate ec + on cl.ref_table = 'EmpCertificate' + and cl.ref_oid = ec.id inner join - employee emp - on emp.id = ec.emp_id + employee emp + on emp.id = ec.emp_id where - cl.last_upd_date >='2013-11-07 15:03:00' - and cl.last_upd_date<='2013-11-08 16:00:00' + cl.last_upd_date >='2013-11-07 15:03:00' + and cl.last_upd_date<='2013-11-08 16:00:00' and emp.is_deleted = 0  ``` -不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致 +4.不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致 5.现有索引可以满足,不需要建索引 @@ -186,12 +190,12 @@ where ```sql select from -stage_poi sp +stage_poi sp where -sp.accurate_result=1 +sp.accurate_result=1 and ( - sp.sync_status=0 - or sp.sync_status=2 + sp.sync_status=0 + or sp.sync_status=2 or sp.sync_status=4 ); @@ -200,6 +204,7 @@ and ( 0.先看看运行多长时间,951条数据6.22秒,真的很慢 951 rows in set (6.22 sec) + 1.先explain,rows达到了361万,type = ALL表明是全表扫描 2.所有字段都应用查询返回记录数,因为是单表查询 0已经做过了951条 @@ -208,6 +213,7 @@ and ( 看一下accurate_result = 1的记录数 +```sql select count(*),accurate_result from stage_poi group by accurate_result; +----------+-----------------+ | count(*) | accurate_result | @@ -216,12 +222,14 @@ select count(*),accurate_result from stage_poi group by accurate_result; | 2114655 | 0 | | 972815 | 1 | +----------+-----------------+ +``` 我们看到accurate_result这个字段的区分度非常低,整个表只有-1,0,1三个值,加上索引也无法锁定特别少量的数据 再看一下sync_status字段的情况 +```sql select count(*),sync_status from stage_poi group by sync_status; +----------+-------------+ | count(*) | sync_status | @@ -229,6 +237,7 @@ select count(*),sync_status from stage_poi group by sync_status; | 3080 | 0 | | 3085413 | 3 | +----------+-------------+ +``` 同样的区分度也很低,根据理论,也不适合建立索引 @@ -239,15 +248,17 @@ select count(*),sync_status from stage_poi group by sync_status; 5.根据建立索引规则,使用如下语句建立索引 -alter table stage_poi add index idx_acc_status(accurate_result,sync_status); +```alter table stage_poi add index idx_acc_status(accurate_result,sync_status);``` 6.观察预期结果,发现只需要200ms,快了30多倍。 952 rows in set (0.20 sec) + 我们再来回顾一下分析问题的过程,单表查询相对来说比较好优化,大部分时候只需要把where条件里面的字段依照规则加上索引就好,如果只是这种“无脑”优化的话,显然一些区分度非常低的列,不应该加索引的列也会被加上索引,这样会对插入、更新性能造成严重的影响,同时也有可能影响其它的查询语句。所以我们第4步调差SQL的使用场景非常关键,我们只有知道这个业务场景,才能更好地辅助我们更好的分析和优化查询语句。 无法优化的语句 +```sql select c.id, c.name, @@ -264,37 +275,37 @@ select c.data_source, from_unixtime(c.created_time) as created_time, from_unixtime(c.last_modified) as last_modified, - c.last_modified_user_id + c.last_modified_user_id from - contact c + contact c inner join - contact_branch cb - on c.id = cb.contact_id + contact_branch cb + on c.id = cb.contact_id inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 10802 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 10802 + and oei.org_category = - 1 order by c.created_time desc limit 0 , 10; - +``` 还是几个步骤 0.先看语句运行多长时间,10条记录用了13秒,已经不可忍受 10 rows in set (13.06 sec) + 1.explain ```sql - +----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+ @@ -312,22 +323,22 @@ rows返回的都非常少,看不到有什么异常情况。我们在看一下 select count(*) from - contact c + contact c inner join - contact_branch cb - on c.id = cb.contact_id + contact_branch cb + on c.id = cb.contact_id inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 10802 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 10802 + and oei.org_category = - 1 +----------+ | count(*) | +----------+ @@ -388,6 +399,7 @@ c.created_time desc limit 0 , ``` 10 rows in set (0.00 sec) + 本以为至此大工告成,但我们在前面的分析中漏了一个细节,先排序再join和先join再排序理论上开销是一样的,为何提升这么多是因为有一个limit!大致执行过程是:mysql先按索引排序得到前10条记录,然后再去join过滤,当发现不够10条的时候,再次去10条,再次join,这显然在内层join过滤的数据非常多的时候,将是灾难的,极端情况,内层一条数据都找不到,mysql还傻乎乎的每次取10条,几乎遍历了这个数据表! 用不同参数的SQL试验下 @@ -408,45 +420,49 @@ select c.data_source, from_unixtime(c.created_time) as created_time, from_unixtime(c.last_modified) as last_modified, - c.last_modified_user_id + c.last_modified_user_id from - contact c + contact c where exists ( select - 1 + 1 from - contact_branch cb + contact_branch cb inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 2875 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 2875 + and oei.org_category = - 1 where - c.id = cb.contact_id - ) + c.id = cb.contact_id + ) order by c.created_time desc limit 0 , 10; Empty set (2 min 18.99 sec) ``` -2 min 18.99 sec!比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。 +2 min 18.99 sec! + +比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。 通过这个例子我们可以看到,并不是所有语句都能优化,而往往我们优化时,由于SQL用例回归时落掉一些极端情况,会造成比原来还严重的后果。所以,第一:不要指望所有语句都能通过SQL优化,第二:不要过于自信,只针对具体case来优化,而忽略了更复杂的情况。 慢查询的案例就分析到这儿,以上只是一些比较典型的案例。我们在优化过程中遇到过超过1000行,涉及到16个表join的“垃圾SQL”,也遇到过线上线下数据库差异导致应用直接被慢查询拖死,也遇到过varchar等值比较没有写单引号,还遇到过笛卡尔积查询直接把从库搞死。再多的案例其实也只是一些经验的积累,如果我们熟悉查询优化器、索引的内部原理,那么分析这些案例就变得特别简单了。 -写在后面的话 +##写在后面的话 + 本文以一个慢查询案例引入了MySQL索引原理、优化慢查询的一些方法论;并针对遇到的典型案例做了详细的分析。其实做了这么长时间的语句优化后才发现,任何数据库层面的优化都抵不上应用系统的优化,同样是MySQL,可以用来支撑Google/FaceBook/Taobao应用,但可能连你的个人网站都撑不住。套用最近比较流行的话:“查询容易,优化不易,且写且珍惜!” -参考 -参考文献如下: +##参考文献如下 + 1.《高性能MySQL》 + 2.《数据结构与算法分析》 diff --git a/Mysql/README.md b/Mysql/README.md index 4c06676..969983e 100644 --- a/Mysql/README.md +++ b/Mysql/README.md @@ -4,7 +4,7 @@ - [字段类型]() - ![mysql中的基本数据类型](https://www.2cto.com/uploadfile/Collfiles/20171201/2017120109334458.png) + ![](https://www.2cto.com/uploadfile/Collfiles/20171201/2017120109334458.png) - char/varchar/text/longtext - enum/set @@ -38,4 +38,14 @@ - [乐观锁和悲观锁]() - [死锁](https://www.cnblogs.com/sivkun/p/7518540.html) - \ No newline at end of file + +### 阅读资料 + +- [MySQL索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) + +- [MySQL索引原理及慢查询优化](https://tech.meituan.com/mysql-index.html) + +- [InnoDB备忘录 - Next-Key Lock](http://zhongmingmao.me/2017/05/19/innodb-next-key-lock/) + +- [MySQL主从复制与读写分离](https://www.cnblogs.com/luckcs/articles/2543607.html) + diff --git "a/Mysql/MySQL\343\200\220explain\343\200\221.md" b/Mysql/explain.md similarity index 100% rename from "Mysql/MySQL\343\200\220explain\343\200\221.md" rename to Mysql/explain.md diff --git a/Mysql/mysql.md b/Mysql/mysql.md index 69a3cac..ee560a9 100644 --- a/Mysql/mysql.md +++ b/Mysql/mysql.md @@ -16,50 +16,10 @@ - [MySQL索引原理及慢查询优化](https://github.com/xianyunyh/PHP-Interview/tree/master/Mysql/MySQL索引原理及慢查询优化.md) -1、选择优化的数据类型。 +参考资料: -更小的数据类型,通常会更好。 +- [聚簇索引和聚簇索引介绍](https://www.cnblogs.com/Jessy/p/3543063.html) -尽量避免使用NULL +- [MySQL笔记](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/MySQL.md) -尽量使用整型作为标志列 -小心使用enum和set。避免使用bit - -尽量使用相同的数据类型存储相似的或者相关的值。 - -## 整数类型 - -tinyint 、smallint、mediaint、int、bigint - -分别占8、16、24、32、64位空间 - -## 字符串类型 - -varchar、char类型 - -char是定长 - -varchar 变长 - -blob和text类型 - -二进制和字符存储 - -## 枚举类型 - -enum("a","b") - -## 日期时间类型 - -datetime 精确到秒 2018-02-22 12:02:23:12 - -date 精确到日期 2018-02-22 - -timestamp 时间戳类型 - -## 位操作类型 - -bit(8) 8位 - -values(b"00000001") diff --git "a/Mysql/\347\264\242\345\274\225.md" "b/Mysql/\347\264\242\345\274\225.md" index 4eaa96a..b203f38 100644 --- "a/Mysql/\347\264\242\345\274\225.md" +++ "b/Mysql/\347\264\242\345\274\225.md" @@ -1,60 +1,163 @@ -**Mysql索引概念:** +## 索引分类 -说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这本书1000页,有500也是目录,它当然效率低,目录是要占纸张的,而索引是要占磁盘空间的。 +**从物理存储角度** -**Mysql索引主要有两种结构:B+tree和hash.** +1. 聚集索引(clustered index) -hash:hsah索引在mysql比较少用,他以把数据的索引以hash形式组织起来,因此当查找某一条记录的时候,速度非常快.当时因为是hash结构,每个键只对应一个值,而且是散列的方式分布.所以他并不支持范围查找和排序等功能. +2. 非聚集索引(non-clustered index)、 二级索引 -B+树:b+tree是mysql使用最频繁的一个索引数据结构,数据结构以平衡树的形式来组织,因为是树型结构,所以更适合用来处理排序,范围查找等功能.相对hash索引,B+树在查找单条记录的速度虽然比不上hash索引,但是因为更适合排序等操作,所以他更受用户的欢迎.毕竟不可能只对数据库进行单条记录的操作. +**从逻辑角度** -**Mysql常见索引:**主键索引、唯一索引、普通索引、全文索引、组合索引 +1. 主键索引:主键索引是一种特殊的唯一索引,不允许有空值 -PRIMARY KEY(主键索引) ALTER TABLE \`table_name\` ADD PRIMARY KEY ( \`column\` ) +2. 普通索引或者单列索引 - UNIQUE(唯一索引) ALTER TABLE \`table_name\` ADD UNIQUE (\`column\`) +3. 多列索引(联合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合 -INDEX(普通索引) ALTER TABLE \`table\_name\` ADD INDEX index\_name ( \`column\` ) +4. 唯一索引或者非唯一索引 -FULLTEXT(全文索引) ALTER TABLE \`table_name\` ADD FULLTEXT ( \`column\` ) +5. 空间索引:空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。 + MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建 -组合索引 ALTER TABLE \`table\_name\` ADD INDEX index\_name ( \`column1\`, \`column2\`, \`column3\` ) +**从数据结构角度** -**Mysql各种索引区别:** +1. B-Tree索引 -普通索引:最基本的索引,没有任何限制 -唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。 +2. Hash索引: + a 仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询 + b 其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引 + c 只有Memory存储引擎显示支持hash索引 -主键索引:它 是一种特殊的唯一索引,不允许有空值。 +3. FULLTEXT索引(现在MyISAM和InnoDB引擎都支持了) -全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间。 +4. R-Tree索引 -组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则。 -**B+Tree** -![](http://p.blog.csdn.net/images/p_blog_csdn_net/manesking/5.JPG) +## 聚簇索引(cluster index) -1. 所有关键字都在叶子结点出现 +**聚簇索引、聚集索引 一个意思**。 -2. 所有叶子结点增加一个链指针 +指索引项的排序方式和表中数据记录排序方式一致的索引。一个表至少有一个聚集索引。 -## 聚集索引和辅助索引、覆盖索引 +1. 如果表设置了主键,则主键就是聚簇索引 -- 聚集索引(主键索引) +2. 如果表没有主键,则会默认第一个NOT NULL,且唯一(UNIQUE)的列作为聚簇索引 -—innodb存储引擎是索引组织表,即表中的数据按照主键顺序存放。而聚集索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的即为整张表的记录数据 +3. 以上都没有,则会默认创建一个隐藏的row_id作为聚簇索引 -—聚集索引的叶子节点称为数据页,数据页,数据页!重要的事说三遍。聚集索引的这个特性决定了索引组织表中的数据也是索引的一部分。 +InnoDB的聚簇索引的叶子节点存储的是行记录(其实是页结构,一个页包含多行数据),InnoDB必须要有至少一个聚簇索引。 -- 辅助索引(二级索引) +由此可见,使用聚簇索引查询会很快,因为可以直接定位到行记录。 -—非主键索引 +> InnoDB的聚簇索引的叶子节点存储的是行记录(其实是页结构,一个页包含多行数据),InnoDB必须要有至少一个聚簇索引。 +> +> 由此可见,使用聚簇索引查询会很快,因为可以直接定位到行记录。 -—叶子节点=键值+书签。Innodb存储引擎的书签就是相应行数据的主键索引值 +## 普通索引 -- 覆盖索引 +普通索引也叫**二级索引**,除聚簇索引外的索引,即非聚簇索引。 -如果查询的列恰好是索引的一部分,那么查询只需要在索引文件上进行,不需要进行到磁盘中找数据,若果查询得列不是索引的一部分则要到磁盘中找数据 +InnoDB的普通索引叶子节点存储的是主键(聚簇索引)的值,而MyISAM的普通索引存储的是记录指针。 -使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为**using index**,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询 +```mysql +mysql> create table user( + -> id int(10) auto_increment, + -> name varchar(30), + -> age tinyint(4), + -> primary key (id), + -> index idx_age (age) + -> )engine=innodb charset=utf8mb4; +insert into user(name,age) values('张三',30); +insert into user(name,age) values('李四',20); +insert into user(name,age) values('王五',40); +insert into user(name,age) values('刘八',10); +``` + + + +### 聚簇索引的结构 + +> 叶子节点存储行信息 + +![MySQL 的覆盖索引与回表](https://user-gold-cdn.xitu.io/2020/2/10/1702e72ac4e50ce0?imageView2/0/w/1280/h/960/format/jpeg/ignore-error/1) + +### 非聚簇索引结构 + +> 非聚簇索引,其叶子节点存储的是聚簇索引的的值 + +![MySQL 的覆盖索引与回表](https://user-gold-cdn.xitu.io/2020/2/10/1702e72abd85ecc6?imageView2/0/w/1280/h/960/format/jpeg/ignore-error/1) + +```sql +select * from user where id =1 +select * from user where age = 20 +``` + +对于聚簇索引查询,需要扫描聚簇索引,一次即可扫描到记录, + +对于非聚簇索引,利用非非聚簇索引,扫到聚簇索引的值,然后会再次到聚簇索引中查找, + +### 回表查询 + +先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据,需要扫描两次索引B+树,它的性能较扫一遍索引树更低。这个过程叫做回表 + +### 索引覆盖 + +查询的结果字段包含所有索引信息,不会回表,比如 + +```sql +select id,age from user where age = 10 +``` + +当age索引一次扫描到聚簇索引的值的时候,正好得到了所有的结果,避免了回表操作。 + +实现: 将被查询的字段,建立到联合索引里去。 + +可以通过`explain` ,查看extra字段。如果是`use index` 则使用了索引,如果是`null` 则进行了回表 + +- count 优化 +- 列查询优化 +- 分页查询优化 + + + +## 索引下推 + +索引下推(index condition pushdown) 指的是Mysql需要一个表中检索数据的时候,会使用索引过滤掉不符合条件的数据,然后再返回给客户端 + +```sql +"select * from user where username like '张%' and age > 10" +``` + +**没有ICP** + +根据(username,age)联合索引查询所有满足名称以“张”开头的索引,然后回表查询出相应的全行数据,然后再筛选出满足年龄小于等于10的用户数据 + +1,获取下一行,首先读索引元组,然后使用索引去查找并读取所有的行 + +2,根据WHERE条件部分,判断数据是否符合。根据判断结果接受或拒绝该行 + +**使用ICP** + +这个过程则会变成这样: + +根据(username,age)联合索引查询所有满足名称以“张”开头的索引,然后直接再筛选出年龄小于等于10的索引,之后再回表查询全行数据 + +![img](https://user-gold-cdn.xitu.io/2019/7/11/16bde887c8272afd?imageView2/0/w/1280/h/960/format/jpeg/ignore-error/1) + +1,获取下一行的索引元组(不是所有行) + +2,根据WHERE条件部分,判断是否可以只通过索引列满足条件。如果不满足,则获取下一行索引元组 + +3,如果满足条件,则通过索引元组去查询并读取所有的行 + +4,根据遗留的WHERE子句中的条件,在当前表中进行判断,根据判断结果接受或者拒绝改行 + +![img](https://user-gold-cdn.xitu.io/2019/7/11/16bde887deb74017?imageView2/0/w/1280/h/960/format/jpeg/ignore-error/1) + +ICP默认启动。可以通过optimizer_switch系统变量去控制它是否开启: + +```ini +SET optimizer_switch = 'index_condition_pushdown=off'; +SET optimizer_switch = 'index_condition_pushdown=on'; +``` diff --git "a/Mysql/\351\224\201.md" "b/Mysql/\351\224\201.md" new file mode 100644 index 0000000..2cfa199 --- /dev/null +++ "b/Mysql/\351\224\201.md" @@ -0,0 +1,64 @@ +## 乐观锁 +乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。通常的做法,是在表中加个版本字段,更新的时候和读取的时候做比对。如果一致则进行修改。 +```sql +select v,name from table where id =1 ;// 假设v = 1 + +update table set name ="test" and v = v+1 where id = 1 and v =1;// 如果v之前有过修改,则此次修改失败。 +``` +## 悲观锁 +与乐观锁相对应的就是悲观锁了。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以 +### 共享锁 [读锁] + +共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,这个就是所谓的共享锁。 +```sql +begin +select id from `table` where id = 1 lock in share mode; + +# 第二个客户端 +update `table` set name = "ddte" where id =1 ;//报错 第一个事务没提交 +``` +### 排它锁 [写锁] +排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。 + +与共享锁类型,在需要执行的语句后面加上for update就可以了(对于Innodb引擎语句后面加上for update表示把此行数据锁定,MyISAM则是锁定整个表。) +```sql +select …… for update; +``` +## MVCC + +mysql的innodb采用的是行锁,而且采用了多版本并发控制来提高读操作的性能。 + +MVVC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)是一种基于多版本的并发控制协议,只有在InnoDB引擎下存在。MVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,你可以把它当成基于多版本号的一种乐观锁。当然,这种乐观锁只在事务级别未提交锁和已提交锁时才会生效。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能 + +### 实现方式 +  InnoDB在每行数据都增加两个隐藏字段,一个记录创建的版本号,一个记录删除的版本号。 +版本链 + +在InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列: + +- trx_id +这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id。 +- roll_pointer +每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本) + +| id | name | trx_id | roll_pointer | +| ---- | ---- | ------ | ------------ | +| 1 | 1 | 50 | 49 | +| | | | | + +在多版本并发控制中,为了保证数据操作在多线程过程中,保证事务隔离的机制,降低锁竞争的压力,保证较高的并发量。在每开启一个事务时,会生成一个事务的版本号,被操作的数据会生成一条新的数据行(临时),但是在提交前对其他事务是不可见的,对于数据的更新(包括增删改)操作成功,会将这个版本号更新到数据的行中,事务提交成功,将新的版本号更新到此数据行中,这样保证了每个事务操作的数据,都是互不影响的,也不存在锁的问题。 +### MVVC下的CRUD +- SELECT: +  当隔离级别是REPEATABLE READ时select操作,InnoDB必须每行数据来保证它符合两个条件: +  1、InnoDB必须找到一个行的版本,它至少要和事务的版本一样老(也即它的版本号不大于事务的版本号)。这保证了不管是事务开始之前,或者事务创建时,或者修改了这行数据的时候,这行数据是存在的。 +  2、这行数据的删除版本必须是未定义的或者比事务版本要大。这可以保证在事务开始之前这行数据没有被删除。 +符合这两个条件的行可能会被当作查询结果而返回。 + + +- INSERT:InnoDB为这个新行记录当前的系统版本号。 + +- DELETE:InnoDB将当前的系统版本号设置为这一行的删除ID。 +- UPDATE:InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为当前的系统版本号。它同时也会将这个版本号写到旧行的删除版本里。 + +这种额外的记录所带来的结果就是对于大多数查询来说根本就不需要获得一个锁。他们只是简单地以最快的速度来读取数据,确保只选择符合条件的行。这个方案的缺点在于存储引擎必须为每一行存储更多的数据,做更多的检查工作,处理更多的善后操作。 +  MVCC只工作在REPEATABLE READ和READ COMMITED隔离级别下。READ UNCOMMITED不是MVCC兼容的,因为查询不能找到适合他们事务版本的行版本;它们每次都只能读到最新的版本。SERIABLABLE也不与MVCC兼容,因为读操作会锁定他们返回的每一行数据。 \ No newline at end of file diff --git "a/PHP/PHP-FPM\351\205\215\347\275\256\351\200\211\351\241\271.md" "b/PHP/PHP-FPM\351\205\215\347\275\256\351\200\211\351\241\271.md" new file mode 100644 index 0000000..a95e8b6 --- /dev/null +++ "b/PHP/PHP-FPM\351\205\215\347\275\256\351\200\211\351\241\271.md" @@ -0,0 +1,214 @@ +## PHP-FPM + +FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。 + +它的功能包括: + +- 支持平滑停止/启动的高级进程管理功能; +- 可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置); +- stdout 和 stderr 日志记录; +- 在发生意外情况的时候能够重新启动并缓存被破坏的 opcode; +- 文件上传优化支持; +- "慢日志" - 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢; +- [fastcgi_finish_request()](http://php.net/manual/zh/function.fastcgi-finish-request.php) - 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等); +- 动态/静态子进程产生; +- 基本 SAPI 运行状态信息(类似Apache的 mod_status); +- 基于 php.ini 的配置文件。 + +### 全局配置选项 + +- pid + + pid文件的位置 + +- error_log + + 错误日志的位置 + +- log_level + + 错误的级别 alert 、error、warning、notice、debug、默认是notice + +- syslog.facility + + 设置何种程序记录消息 默认daemon + +- syslog.ident + + 为每条消息 添加前缀 + +- emergency_restart_interval + + 如果子进程在 *emergency_restart_interval* 设定的时间内收到该参数设定次数的 SIGSEGV 或者 SIGBUS退出信息号,则FPM会重新启动。0 表示“关闭该功能”。默认值:0(关闭) + +- process_control_timeout + + 设置子进程接受主进程复用信号的超时时间 可用单位:s(秒),m(分),h(小时)或者 d(天)。默认单位:s(秒)。默认值:0(关闭)。 + +- process_max + + fork的最大的fpm的进程数 + +- process.priority + + 设置master进程的nice(2) 优先级 + +- daemonize + + 设置php-fpm后台运行。默认是yes + +- rlimit_core + + 设置master 进程打开的core最大的尺寸 + +- events.mechaism + + 设置fpm的使用的事件机制 select、pool、epoll、kqueue (*BSD)、port (Solaris ) + +- systemd_interval + + 使用systemd集成的fpm时,设置间歇秒数 + +### 运行时配置 + +- listen + + 设置接受的fastcgi请求的地址可用格式为:'ip:port','port','/path/to/unix/socket'。每个进程池都需要设置 + +- listen.backlog + + 设置backlog的最大值 + +- listen.allowed_clients + + 设置允许链接到fastcgi的ip地址 + +- listen.owner listen.group listen.mode + + 设置权限 + +- user group + + fpm运行的Unix用户 必须设置的 + +- pm + + 设置进程管理器如何管理子进程 + + - static 子进程是固定的 = *pm.max_children* + - *ondemand* 进程在有需求时才产生 + - *dynamic*  子进程的数量在下面配置的基础上动态设置 *pm.max_children*,*pm.start_servers*,*pm.min_spare_servers*,*pm.max_spare_servers*。 + +- pm.max_children + + pm设置为staic时。表示创建的子进程的数量。 + + pm为dynamic时,表示最大可创建的进程数 + +- pm.start_servers + + 设置启动时创建的子进程的数目。仅在 *pm* 设置为 *dynamic* 时使用 。就是初始化创建的进程数 + +- pm.min_spare_servers + + 设置空闲进程的最大数目 + +- pm.process_idle_timeout + + 秒。多久之后结束空闲进程 + +- pm.max_requests + + 设置每个子进程重生之前的服务的请求数。 + +- pm.status_path + + fpm状态的页面的地址 + +- ping.path + + fpm监控页面的ping的地址 + +- ping.resource + + 用于定于ping请求的返回响应 默认是pong + +- request_terminate_timeout + + 设置单个请求的超时中止时间。 + +- request_slowlog_timeout + + 当一个请求该设置的超时时间后,就会将对应的 PHP 调用堆栈信息完整写入到慢日志中 + +- slowlog + + 慢请求的记录日志 + +- rlimit_files + + 设置打开文件描述符的限制 + +- access.log + + 访问日志 + +- access.format + + acces log的格式 + +### 优化 + +#### 内核调优 + +- 调整linux内核打开文件的数量。 + +```shell +echo `ulimit -HSn 65536` >> /etc/profile +echo `ulimit -HSn 65536` >> /etc/rc.local +source /etc/profile +``` + +#### PHP-FPM配置调优 + +- 进程数调整 + +>pm.max_children = 300; **静态方式**下开启的php-fpm进程数量    +> +>pm.start_servers = 20; **动态方式**下的起始php-fpm进程数量    +> +>pm.min_spare_servers = 5; **动态方式**下的最小php-fpm进程数量    +> +>pm.max_spare_servers = 35; **动态方式**下的最大php-fpm进程数量 +> +>request_slowlog_timeout = 2; 开启慢日志 +>slowlog = log/$pool.log.slow; 慢日志路径 +> +>rlimit_files = 1024; 增加php-fpm打开文件描述符的限制 + +**一般来说一台服务器正常情况下每一个php-cgi所耗费的内存在20M左右 。** + +用内存/20 就大概算出最大的进程数。 + +一般初始化的进程有一个类似的公式 + +``` +start_servers = min_spare_servers + (max_spare_servers - min_spare_servers) / 2; +``` + + + +如果长时间没有得到处理的请求就会出现504 Gateway Time-out这个错误,而正在处理的很累的那几个php-cgi如果遇到了问题就会出现502 Bad gateway这个错误 + +- **最大请求数** + + 最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn一个新的。 + + 这个配置的主要目的是避免php解释器或程序引用的第三方库造成的内存泄露。 + + ​ pm.max_requests = 10240 + +- 最长执行时间 + + 最大执行时间在php.ini和php-fpm.conf里都可以配置,配置项分别为max_execution_time和request_terminate_timeout。 + diff --git "a/PHP/PHP-Zval\347\273\223\346\236\204.md" "b/PHP/PHP-Zval\347\273\223\346\236\204.md" new file mode 100644 index 0000000..f328410 --- /dev/null +++ "b/PHP/PHP-Zval\347\273\223\346\236\204.md" @@ -0,0 +1,331 @@ +## 2.1 变量的内部实现 + +变量是一个语言实现的基础,变量有两个组成部分:变量名、变量值,PHP中可以将其对应为:zval、zend_value,这两个概念一定要区分开,PHP中变量的内存是通过引用计数进行管理的,而且PHP7中引用计数是在zend_value而不是zval上,变量之间的传递、赋值通常也是针对zend_value。 + +PHP中可以通过`$`关键词定义一个变量:`$a;`,在定义的同时可以进行初始化:`$a = "hi~";`,注意这实际是两步:定义、初始化,只定义一个变量也是可以的,可以不给它赋值,比如: +```php +$a; +$b = 1; +``` +这段代码在执行时会分配两个zval。 + +接下来我们具体看下变量的结构以及不同类型的实现。 + +### 2.1.1 变量的基础结构 +```c +//zend_types.h +typedef struct _zval_struct zval; + +typedef union _zend_value { + zend_long lval; //int整形 + double dval; //浮点型 + zend_refcounted *counted; + zend_string *str; //string字符串 + zend_array *arr; //array数组 + zend_object *obj; //object对象 + zend_resource *res; //resource资源类型 + zend_reference *ref; //引用类型,通过&$var_name定义的 + zend_ast_ref *ast; //下面几个都是内核使用的value + zval *zv; + void *ptr; + zend_class_entry *ce; + zend_function *func; + struct { + uint32_t w1; + uint32_t w2; + } ww; +} zend_value; + +struct _zval_struct { + zend_value value; //变量实际的value + union { + struct { + ZEND_ENDIAN_LOHI_4( //这个是为了兼容大小字节序,小字节序就是下面的顺序,大字节序则下面4个顺序翻转 + zend_uchar type, //变量类型 + zend_uchar type_flags, //类型掩码,不同的类型会有不同的几种属性,内存管理会用到 + zend_uchar const_flags, + zend_uchar reserved) //call info,zend执行流程会用到 + } v; + uint32_t type_info; //上面4个值的组合值,可以直接根据type_info取到4个对应位置的值 + } u1; + union { + uint32_t var_flags; + uint32_t next; //哈希表中解决哈希冲突时用到 + uint32_t cache_slot; /* literal cache slot */ + uint32_t lineno; /* line number (for ast nodes) */ + uint32_t num_args; /* arguments number for EX(This) */ + uint32_t fe_pos; /* foreach position */ + uint32_t fe_iter_idx; /* foreach iterator index */ + } u2; //一些辅助值 +}; +``` +`zval`结构比较简单,内嵌一个union类型的`zend_value`保存具体变量类型的值或指针,`zval`中还有两个union:`u1`、`u2`: +* __u1:__ 它的意义比较直观,变量的类型就通过`u1.v.type`区分,另外一个值`type_flags`为类型掩码,在变量的内存管理、gc机制中会用到,第三部分会详细分析,至于后面两个`const_flags`、`reserved`暂且不管 +* __u2:__ 这个值纯粹是个辅助值,假如`zval`只有:`value`、`u1`两个值,整个zval的大小也会对齐到16byte,既然不管有没有u2大小都是16byte,把多余的4byte拿出来用于一些特殊用途还是很划算的,比如next在哈希表解决哈希冲突时会用到,还有fe_pos在foreach会用到...... + +从`zend_value`可以看出,除`long`、`double`类型直接存储值外,其它类型都为指针,指向各自的结构。 + +### 2.1.2 类型 +`zval.u1.type`类型: +```c +/* regular data types */ +#define IS_UNDEF 0 +#define IS_NULL 1 +#define IS_FALSE 2 +#define IS_TRUE 3 +#define IS_LONG 4 +#define IS_DOUBLE 5 +#define IS_STRING 6 +#define IS_ARRAY 7 +#define IS_OBJECT 8 +#define IS_RESOURCE 9 +#define IS_REFERENCE 10 + +/* constant expressions */ +#define IS_CONSTANT 11 +#define IS_CONSTANT_AST 12 + +/* fake types */ +#define _IS_BOOL 13 +#define IS_CALLABLE 14 + +/* internal types */ +#define IS_INDIRECT 15 +#define IS_PTR 17 +``` + +#### 2.1.2.1 标量类型 +最简单的类型是true、false、long、double、null,其中true、false、null没有value,直接根据type区分,而long、double的值则直接存在value中:zend_long、double,也就是标量类型不需要额外的value指针。 + +#### 2.1.2.2 字符串 +PHP中字符串通过`zend_string`表示: +```c +struct _zend_string { + zend_refcounted_h gc; + zend_ulong h; /* hash value */ + size_t len; + char val[1]; +}; +``` +* __gc:__ 变量引用信息,比如当前value的引用数,所有用到引用计数的变量类型都会有这个结构,3.1节会详细分析 +* __h:__ 哈希值,数组中计算索引时会用到 +* __len:__ 字符串长度,通过这个值保证二进制安全 +* __val:__ 字符串内容,变长struct,分配时按len长度申请内存 + +事实上字符串又可具体分为几类:IS_STR_PERSISTENT(通过malloc分配的)、IS_STR_INTERNED(php代码里写的一些字面量,比如函数名、变量值)、IS_STR_PERMANENT(永久值,生命周期大于request)、IS_STR_CONSTANT(常量)、IS_STR_CONSTANT_UNQUALIFIED,这个信息通过flag保存:zval.value->gc.u.flags,后面用到的时候再具体分析。 + +#### 2.1.2.3 数组 +array是PHP中非常强大的一个数据结构,它的底层实现就是普通的有序HashTable,这里简单看下它的结构,下一节会单独分析数组的实现。 + +```c +typedef struct _zend_array HashTable; + +struct _zend_array { + zend_refcounted_h gc; //引用计数信息,与字符串相同 + union { + struct { + ZEND_ENDIAN_LOHI_4( + zend_uchar flags, + zend_uchar nApplyCount, + zend_uchar nIteratorsCount, + zend_uchar reserve) + } v; + uint32_t flags; + } u; + uint32_t nTableMask; //计算bucket索引时的掩码 + Bucket *arData; //bucket数组 + uint32_t nNumUsed; //已用bucket数 + uint32_t nNumOfElements; //已有元素数,nNumOfElements <= nNumUsed,因为删除的并不是直接从arData中移除 + uint32_t nTableSize; //数组的大小,为2^n + uint32_t nInternalPointer; //数值索引 + zend_long nNextFreeElement; + dtor_func_t pDestructor; +}; +``` +#### 2.1.2.4 对象/资源 +```c +struct _zend_object { + zend_refcounted_h gc; + uint32_t handle; + zend_class_entry *ce; //对象对应的class类 + const zend_object_handlers *handlers; + HashTable *properties; //对象属性哈希表 + zval properties_table[1]; +}; + +struct _zend_resource { + zend_refcounted_h gc; + int handle; + int type; + void *ptr; +}; +``` +对象比较常见,资源指的是tcp连接、文件句柄等等类型,这种类型比较灵活,可以随意定义struct,通过ptr指向,后面会单独分析这种类型,这里不再多说。 + +#### 2.1.2.5 引用 +引用是PHP中比较特殊的一种类型,它实际是指向另外一个PHP变量,对它的修改会直接改动实际指向的zval,可以简单的理解为C中的指针,在PHP中通过`&`操作符产生一个引用变量,也就是说不管以前的类型是什么,`&`首先会创建一个`zend_reference`结构,其内嵌了一个zval,这个zval的value指向原来zval的value(如果是布尔、整形、浮点则直接复制原来的值),然后将原zval的类型修改为IS_REFERENCE,原zval的value指向新创建的`zend_reference`结构。 +```c +struct _zend_reference { + zend_refcounted_h gc; + zval val; +}; +``` +结构非常简单,除了公共部分`zend_refcounted_h`外只有一个`val`,举个示例看下具体的结构关系: +```php +$a = "time:" . time(); //$a -> zend_string_1(refcount=1) +$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1) +``` +注意:引用只能通过`&`产生,无法通过赋值传递,比如: +```php +$a = "time:" . time(); //$a -> zend_string_1(refcount=1) +$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1) +$c = $b; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=2) + //$c -> --- +``` +`$b = &$a`这时候`$a`、`$b`的类型是引用,但是`$c = $b`并不会直接将`$b`赋值给`$c`,而是把`$b`实际指向的zval赋值给`$c`,如果想要`$c`也是一个引用则需要这么操作: +```php +$a = "time:" . time(); //$a -> zend_string_1(refcount=1) +$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1) +$c = &$b;/*或$c = &$a*/ //$a,$b,$c -> zend_reference_1(refcount=3) -> zend_string_1(refcount=1) +``` +这个也表示PHP中的 __引用只可能有一层__ ,__不会出现一个引用指向另外一个引用的情况__ ,也就是没有C语言中`指针的指针`的概念。 + +### 2.1.3 内存管理 +接下来分析下变量的分配、销毁。 + +在分析变量内存管理之前我们先自己想一下可能的实现方案,最简单的处理方式:定义变量时alloc一个zval及对应的value结构(ref/arr/str/res...),赋值、函数传参时硬拷贝一个副本,这样各变量最终的值完全都是独立的,不会出现多个变量同时共用一个value的情况,在执行完以后直接将各变量及value结构free掉。 + +这种方式是可行的,而且内存管理也很简单,但是,硬拷贝带来的一个问题是效率低,比如我们定义了一个变量然后赋值给另外一个变量,可能后面都只是只读操作,假如硬拷贝的话就会有多余的一份数据,这个问题的解决方案是: __引用计数+写时复制__ 。PHP变量的管理正是基于这两点实现的。 + +#### 2.1.3.1 引用计数 +引用计数是指在value中增加一个字段`refcount`记录指向当前value的数量,变量复制、函数传参时并不直接硬拷贝一份value数据,而是将`refcount++`,变量销毁时将`refcount--`,等到`refcount`减为0时表示已经没有变量引用这个value,将它销毁即可。 +```php +$a = "time:" . time(); //$a -> zend_string_1(refcount=1) +$b = $a; //$a,$b -> zend_string_1(refcount=2) +$c = $b; //$a,$b,$c -> zend_string_1(refcount=3) + +unset($b); //$b = IS_UNDEF $a,$c -> zend_string_1(refcount=2) +``` +引用计数的信息位于给具体value结构的gc中: +```c +typedef struct _zend_refcounted_h { + uint32_t refcount; /* reference counter 32-bit */ + union { + struct { + ZEND_ENDIAN_LOHI_3( + zend_uchar type, + zend_uchar flags, /* used for strings & objects */ + uint16_t gc_info) /* keeps GC root number (or 0) and color */ + } v; + uint32_t type_info; + } u; +} zend_refcounted_h; +``` +从上面的zend_value结构可以看出并不是所有的数据类型都会用到引用计数,`long`、`double`直接都是硬拷贝,只有value是指针的那几种类型才__可能__会用到引用计数。 + +下面再看一个例子: +```php +$a = "hi~"; +$b = $a; +``` +猜测一下变量`$a/$b`的引用情况。 + +这个不跟上面的例子一样吗?字符串`"hi~"`有`$a/$b`两个引用,所以`zend_string1(refcount=2)`。但是这是错的,gdb调试发现上面例子zend_string的引用计数为0。这是为什么呢? +```c +$a,$b -> zend_string_1(refcount=0,val="hi~") +``` + +事实上并不是所有的PHP变量都会用到引用计数,标量:true/false/double/long/null是硬拷贝自然不需要这种机制,但是除了这几个还有两个特殊的类型也不会用到:interned string(内部字符串,就是上面提到的字符串flag:IS_STR_INTERNED)、immutable array,它们的type是`IS_STRING`、`IS_ARRAY`,与普通string、array类型相同,那怎么区分一个value是否支持引用计数呢?还记得`zval.u1`中那个类型掩码`type_flag`吗?正是通过这个字段标识的,这个字段除了标识value是否支持引用计数外还有其它几个标识位,按位分割,注意:`type_flag`与`zval.value->gc.u.flag`不是一个值。 + +支持引用计数的value类型其`zval.u1.type_flag` __包含__ (注意是&,不是等于)`IS_TYPE_REFCOUNTED`: +```c +#define IS_TYPE_REFCOUNTED (1<<2) +``` +下面具体列下哪些类型会有这个标识: +```c +| type | refcounted | ++----------------+------------+ +|simple types | | +|string | Y | +|interned string | | +|array | Y | +|immutable array | | +|object | Y | +|resource | Y | +|reference | Y | +``` +simple types很显然用不到,不再解释,string、array、object、resource、reference有引用计数机制也很容易理解,下面具体解释下另外两个特殊的类型: +* __interned string:__ 内部字符串,这是种什么类型?我们在PHP中写的所有字符都可以认为是这种类型,比如function name、class name、variable name、静态字符串等等,我们这样定义:`$a = "hi~";`后面的字符串内容是唯一不变的,这些字符串等同于C语言中定义在静态变量区的字符串:`char *a = "hi~";`,这些字符串的生命周期为request期间,request完成后会统一销毁释放,自然也就无需在运行期间通过引用计数管理内存。 + +* __immutable array:__ 只有在用opcache的时候才会用到这种类型,不清楚具体实现,暂时忽略。 + +#### 2.1.3.2 写时复制 +上一小节介绍了引用计数,多个变量可能指向同一个value,然后通过refcount统计引用数,这时候如果其中一个变量试图更改value的内容则会重新拷贝一份value修改,同时断开旧的指向,写时复制的机制在计算机系统中有非常广的应用,它只有在必要的时候(写)才会发生硬拷贝,可以很好的提高效率,下面从示例看下: + +```php +$a = array(1,2); +$b = &$a; +$c = $a; + +//发生分离 +$b[] = 3; +``` +不是所有类型都可以copy的,比如对象、资源,事实上只有string、array两种支持,与引用计数相同,也是通过`zval.u1.type_flag`标识value是否可复制的: +```c +#define IS_TYPE_COPYABLE (1<<4) +``` +```c +| type | copyable | ++----------------+------------+ +|simple types | | +|string | Y | +|interned string | | +|array | Y | +|immutable array | | +|object | | +|resource | | +|reference | | +``` +__copyable__ 的意思是当value发生duplication时是否需要或者能够copy,这个具体有两种情形下会发生: +* a.从 __literal变量区__ 复制到 __局部变量区__ ,比如:`$a = [];`实际会有两个数组,而`$a = "hi~";//interned string`则只有一个string +* b.局部变量区分离时(写时复制):如改变变量内容时引用计数大于1则需要分离,`$a = [];$b = $a; $b[] = 1;`这里会分离,类型是array所以可以复制,如果是对象:`$a = new user;$b = $a;$a->name = "dd";`这种情况是不会复制object的,$a、$b指向的对象还是同一个 + +具体literal、局部变量区变量的初始化、赋值后面编译、执行两篇文章会具体分析,这里知道变量有个`copyable`的属性就行了。 + +#### 2.1.3.3 变量回收 +PHP变量的回收主要有两种:主动销毁、自动销毁。主动销毁指的就是 __unset__ ,而自动销毁就是PHP的自动管理机制,在return时减掉局部变量的refcount,即使没有显式的return,PHP也会自动给加上这个操作,另外一个就是写时复制时会断开原来value的指向,这时候也会检查断开后旧value的refcount。 + +#### 2.1.3.4 垃圾回收 +PHP变量的回收是根据refcount实现的,当unset、return时会将变量的引用计数减掉,如果refcount减到0则直接释放value,这是变量的简单gc过程,但是实际过程中出现gc无法回收导致内存泄漏的bug,先看下一个例子: + +```php +$a = [1]; +$a[] = &$a; + +unset($a); +``` +可以看到,`unset($a)`之后由于数组中有子元素指向`$a`,所以`refcount > 0`,无法通过简单的gc机制回收,这种变量就是垃圾,垃圾回收器要处理的就是这种情况,目前垃圾只会出现在array、object两种类型中,所以只会针对这两种情况作特殊处理:当销毁一个变量时,如果发现减掉refcount后仍然大于0,且类型是IS_ARRAY、IS_OBJECT则将此value放入gc可能垃圾双向链表中,等这个链表达到一定数量后启动检查程序将所有变量检查一遍,如果确定是垃圾则销毁释放。 + +标识变量是否需要回收也是通过`u1.type_flag`区分的: +```c +#define IS_TYPE_COLLECTABLE +``` +```c +| type | collectable | ++----------------+-------------+ +|simple types | | +|string | | +|interned string | | +|array | Y | +|immutable array | | +|object | Y | +|resource | | +|reference | | +``` + + +**阅读资料** + +- [PHP zval实现](https://github.com/pangudashu/php7-internal/blob/master/2/zval.md) +- [Internal-value-representation-in-PHP-7](http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html) +- [php7-internal](https://github.com/laruence/php7-internal) \ No newline at end of file diff --git a/PHP/PHP7-HashTable.md b/PHP/PHP7-HashTable.md new file mode 100644 index 0000000..c2e5502 --- /dev/null +++ b/PHP/PHP7-HashTable.md @@ -0,0 +1,117 @@ +![img](https://images0.cnblogs.com/blog2015/444975/201503/091012060652318.png) + +上图是PHP5 hashtable的实现 + +新的zval结构 + +```c +struct _zval_struct { + zend_value value; + union { + struct { + ZEND_ENDIAN_LOHI_4( + zend_uchar type, + zend_uchar type_flags, + zend_uchar const_flags, + zend_uchar reserved + ) + } v; + uint32_t type_info; + } u1; + union { + uint32_t var_flags; + uint32_t next; /* hash collision chain */ + uint32_t cache_slot; /* literal cache slot */ + uint32_t lineno; /* line number (for ast nodes) */ + } u2; +}; +``` + +按照posix标准,一般整形对应的*_t类型为: 1字节     uint8_t 2字节     uint16_t 4字节     uint32_t 8字节     uint64_t + +PHP7中的zval结构包括三个部分。第一个是value。zend_value是一个联合体。保存任何类型的数据 + +第二部分是是四个字节的typeinfo.包含真正变量的类型。 + +第三部分是一个联合体。也是4个字节。辅助字段。 + +新的zval的实现不再使用引用计算。避免了两次计数/ + +新版HashTable的实现 + +```c +# 老版本 +typedef struct bucket { + ulong h; /* Used for numeric indexing */ + uint nKeyLength; + void *pData; + void *pDataPtr; + struct bucket *pListNext; /* 指向哈希链表中下一个元素 */ + struct bucket *pListLast; /* 指向哈希链表中的前一个元素 */ + struct bucket *pNext; /* 相同哈希值的下一个元素(哈希冲突用) */ + struct bucket *pLast; /* 相同哈希值的上一个元素(哈希冲突用) */ + const char *arKey; + +} Bucket; + + + +# PHP7 + +typedef struct _Bucket { + zval val; + zend_ulong h; /* hash value (or numeric index) */ + zend_string *key; /* string key or NULL for numerics */ +} Bucket; +``` + +新的**HashTable**中,hash链表的构建工作由 **HashTable->arHash** 来承担,而解决hash冲突的链表则被放到了**_zval_struct**了 + +h 是hash的值。key 是字符串 val 是值。 + +比如arr["hello"] = "111"; hash(hello) = h hello =k + + 111 = val + +```c +typedef struct _HashTable { + union { + struct { + ZEND_ENDIAN_LOHI_3( + zend_uchar flags, + zend_uchar nApplyCount, /* 循环遍历保护 */ + uint16_t reserve) + } v; + uint32_t flags; + } u; + + uint32_t nTableSize; /* hash表的大小 */ + uint32_t nTableMask; /* 掩码,用于根据hash值计算存储位置,永远等于nTableSize-1 */ + uint32_t nNumUsed; /* arData数组已经使用的数量 */ + uint32_t nNumOfElements; /* hash表中元素个数 */ + uint32_t nInternalPointer; /* 用于HashTable遍历 */ + zend_long nNextFreeElement; /* 下一个空闲可用位置的数字索引 */ + Bucket *arData; /* 存放实际数据 */ + uint32_t *arHash; /* Hash表 */ + dtor_func_t pDestructor; /* 析构函数 */ + +} HashTable; +``` + +HashTable中另外一个非常重要的值`arData`,这个值指向存储元素数组的第一个Bucket + +HashTable中有两个非常相近的值:`nNumUsed`、`nNumOfElements`,`nNumOfElements`表示哈希表已有元素数,那这个值不跟`nNumUsed`一样吗?为什么要定义两个呢?实际上它们有不同的含义,当将一个元素从哈希表删除时并不会将对应的Bucket移除,而是将Bucket存储的zval标示为`IS_UNDEF`,只有扩容时发现nNumOfElements与nNumUsed相差达到一定数量(这个数量是:`ht->nNumUsed - ht->nNumOfElements > (ht->nNumOfElements >> 5)`)时才会将已删除的元素全部移除,重新构建哈希表。所以`nNumUsed`>=`nNumOfElements`。 + +arData数组保存了所有的buckets(也就是数组的元素),这个数组被分配的内存大小为2的幂次方,它被保存在nTableSize这个字段(最小值为8)。数组中实际保存的元素的个数被保存在nNumOfElements这个字段。注意这个数组直接包含Bucket结构。老的Hashtable的实现是使用一个指针数组来保存分开分配的buckets,这意味着需要更多的分配和释放操作(alloc/frees),需要为冗余信息及额外的指针分配内存。 + +### hashtable 查找 + +arHash这个数组,这个数组由unit32_t类型的值组成。arHash数组跟arData的大小一样(都是nTableSize),并且都被分配了一段连续的内存区块。 + +hash函数(DJBX33A用于字符串的键,DJBX33A是PHP用到的一种hash算法)返回的hash值一般是32位或者64位的整型数,这个数有点大,不能直接作为hash数组的索引。首先通过求余操作将这个数调整到hashtable的大小的范围之内。 求余是通过计算hash & (ht->nTableSize - 1) + +参考资料 + +- [http://gywbd.github.io/posts/2014/12/php7-new-hashtable-implementation.html](http://gywbd.github.io/posts/2014/12/php7-new-hashtable-implementation.html) +- [https://blog.csdn.net/pangudashu/article/details/53419992](https://blog.csdn.net/pangudashu/article/details/53419992) +- [https://blog.csdn.net/heiyeshuwu/article/details/44259865](https://blog.csdn.net/heiyeshuwu/article/details/44259865) \ No newline at end of file diff --git a/PHP/PHP8.1.md b/PHP/PHP8.1.md new file mode 100644 index 0000000..6987891 --- /dev/null +++ b/PHP/PHP8.1.md @@ -0,0 +1,91 @@ +PHP8.1在2021年11月25日发布了。又带来了很多很特性和性能改进。 + +## 枚举 +Enum 只支持整型和字符串两种类型 +```php +enum Status: int { + case SUCCESS = 0 + case ERROR = 1 +} + +function test (Status $status) { + +} +test(Status::SUCCESS) +``` + +### 枚举属性和方法 + +枚举有两个属性 `name` 、`value` +```php + $status->name; + $status->value; +``` +Enum 提供了 from () 方法来通过选项 value 的值来获取对应的选项 + +```php +dump(Status::from(0)); + +``` + +## 数组解包 +```php +$array_1 = [ + 'key1' => 'foo', + 'key2' => 'bar' +]; +$array_2 = [ + 'key3' => 'baz', + 'key4' => 'qux' +]; + +$array_unpacked = [...$array_1, ...$array_2]; +dd($array_unpacked); +``` +新增`array_is_list ` 判断是否是从 0 开始递增的数字数组 +```php +dump(array_is_list(['apple', 'orange'])); +``` + +## 类相关 + +### 只读属性readonly + +只读属性只允许初始化一次,修改 readonly 属性就会报错,只能在类的内部使用。 + +```php +class User { + public readonly int $uid; + + public function __construct(int $uid) { + $this->uid = $uid; + } +} +$user = new User(12) +$user->uid = 1;//error +``` +### final 类常量 + +```php +class Foo { + final public const TEST = '1'; +} + +``` + +## 函数相关 + +1. First-class Callable + +```php +$callable = strtoupper(...); +echo $callable('hello, world') . PHP_EOL; +``` +2. Never 返回类型 + +```php +function redirect(string $url): never { + header('Location: ' . $url); + exit(); +} +``` diff --git a/PHP/PHP8.2.md b/PHP/PHP8.2.md new file mode 100644 index 0000000..c0d6206 --- /dev/null +++ b/PHP/PHP8.2.md @@ -0,0 +1,72 @@ +## PHP8.2的变化 +- [PHP8.2](https://www.php.net/releases/8.2/zh.php) +### 只读类 +使用`readonly` 修饰类名 +```php +title = $title; + $this->status = $status; + } +} +``` +### 析取范式 (DNF)类型 +简单的理解,就是定义参数支持交集和并集,'组合并集和交集类型时,交集类型必须用括号进行分组' +```php +class Foo { + public function bar((A&B)|null $entity) { + return $entity; + } +} +``` +### 允许 null、false 和 true 作为独立类型 +```php + +function f(): false { + return false; +} +function f1(): true { + return true; +} +function f2(): null { + return null; +} +``` +### 新的“随机”扩展 +`\Random\Randomizer` 类提供了一个高级接口来使用引擎的随机性来生成随机整数、随机排列数组或字符串、选择随机数组键等。 + +### Traits 中允许常量 +```php +trait T +{ + public const CONSTANT = 1; +} +``` +### 弃用动态属性 +动态属性的创建已被弃用,以帮助避免错误和拼写错误,除非该类通过使用 `#[\AllowDynamicProperties]` 属性来选择。`stdClass` 允许动态属性。 +__get/__set 魔术方法的使用不受此更改的影响。 +```php +class User +{ + public $name; +} + +$user = new User(); +$user->last_name = 'Doe'; // Deprecated notice + +$user = new stdClass(); +$user->last_name = 'Doe'; // Still allowed +``` + +## 弃用和向后不兼容 +- 弃用 ${} 字符串插值。 +- 弃用 utf8_encode 和 utf8_decode 函数。 +- DateTime::createFromImmutable 和 DateTimeImmutable::createFromMutable 方法暂定返回类型为 static。 +- strtolower 和 strtoupper 函数不再对语言环境敏感。 diff --git a/PHP/PHP8.md b/PHP/PHP8.md new file mode 100644 index 0000000..8644cc8 --- /dev/null +++ b/PHP/PHP8.md @@ -0,0 +1,78 @@ +## PHP8新特性 + +#### 1. 命名参数 + +命名参数实现了我们调用函数的时候,不用严格函数的定义顺序。 + +```php +function test($a,$b,$c) { + echo sprintf("a=%s,b=%s,c=%s \n",$a,$b,$c); +} +test("1",c:'2',b:"3"); +test(c: 'c',b: '2',a:"1"); +``` + +### 2. 注解 + + PHP 原生语法来使用结构化的元数据 + +```php +#[Route("/api/posts/{id}")] +function Attribute() { +} +$ref = new ReflectionFunction("Attribute"); +var_dump($ref->getAttributes("Route")[0]->getName()); //Route +var_dump($ref->getAttributes("Route")[0]->getArguments());// +``` + +### 3. 联合类型 + +联合类型 就是一个类型可以多个类型的其中一个 + +```php +class C +{ + private string|int $name; + + public function setName($name){ + $this->name = $name; + echo $this->name.PHP_EOL; + } +} + +$c = new C(); +$c->setName(1); +$c->setName("123"); +$c->setName([]);//error +``` + +### 4. Match表达式 + +新的 match 类似于 switch,并具有以下功能: + +- Match 是一个表达式,它可以储存到变量中亦可以直接返回。 +- Match 分支仅支持单行,它不需要一个 break; 语句。 +- Match 使用严格比较 + +```php +echo match (8.0) { + '8.0' => "Oh no!", + 8.0 => "This is what I expected", +}; +``` + +### 5. **字符串与数字的比较逻辑** + +```php +#php8 +0 == 'foobar' // false +#php7 +0 == 'foobar' // true + +``` + +### 6. JIT 即时编译 + +### 7. 新的类、接口、函数 + +- `str_contains` 字符串包含 、`str_starts_with` 以字符串开始、`str_ends_with` diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/0.php\345\234\250unix\345\271\263\345\217\260\345\256\211\350\243\205.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/0.php\345\234\250unix\345\271\263\345\217\260\345\256\211\350\243\205.md" index 711da56..65aed0a 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/0.php\345\234\250unix\345\271\263\345\217\260\345\256\211\350\243\205.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/0.php\345\234\250unix\345\271\263\345\217\260\345\256\211\350\243\205.md" @@ -1,22 +1,22 @@ -在unix平台下安装PHP的方法有 使用配置喝编译过程或者使用预编译的包。编译需要的知识和软件 - -- 基础的 Unix 技能(有能力操作“make”和一种 C 语言编译器) -- 一个 ANSI C 语言编译器 -- 一个 web 服务器 -- 任何模块特需的组件(例如 GD 和 PDF 库等) -- autoconf: 2.13+(PHP < 5.4.0),2.59+(PHP >= 5.4.0) -- automake: 1.4+ -- libtool: 1.4.x+(除了 1.4.2) -- re2c: 版本 0.13.4 或更高 -- flex: 版本 2.5.4(PHP <= 5.2) -- bison: 版本 1.28(建议),1.35 或 1.75 - - - -## PHP和Apache的安装整合 - -以下只是简单安装,很多配置选项可以查到对应的文档 - -apache文档 - +在unix平台下安装PHP的方法有 使用配置喝编译过程或者使用预编译的包。编译需要的知识和软件 + +- 基础的 Unix 技能(有能力操作“make”和一种 C 语言编译器) +- 一个 ANSI C 语言编译器 +- 一个 web 服务器 +- 任何模块特需的组件(例如 GD 和 PDF 库等) +- autoconf: 2.13+(PHP < 5.4.0),2.59+(PHP >= 5.4.0) +- automake: 1.4+ +- libtool: 1.4.x+(除了 1.4.2) +- re2c: 版本 0.13.4 或更高 +- flex: 版本 2.5.4(PHP <= 5.2) +- bison: 版本 1.28(建议),1.35 或 1.75 + + + +## PHP和Apache的安装整合 + +以下只是简单安装,很多配置选项可以查到对应的文档 + +apache文档 + [apache文档]: http://httpd.apache.org/docs/current/ \ No newline at end of file diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/1.\345\237\272\346\234\254\350\257\255\346\263\225.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/1.\345\237\272\346\234\254\350\257\255\346\263\225.md" index 2db98e8..ee9a1eb 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/1.\345\237\272\346\234\254\350\257\255\346\263\225.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/1.\345\237\272\346\234\254\350\257\255\346\263\225.md" @@ -1,243 +1,243 @@ -## 基本语法 - -php的标记使用`` 也可以使用`` (需要打开短标签) 建议使用前者 - -> 文件末尾的 PHP 代码段结束标记可以不要,有些情况下当使用 [include](http://php.net/manual/zh/function.include.php) 或者 [require](http://php.net/manual/zh/function.require.php) 时省略掉会更好些,这样不期望的空白符就不会出现在文件末尾,之后仍然可以输出响应标头。在使用输出缓冲时也很便利,就不会看到由包含文件生成的不期望的空白符 - -```php - -``` - -## 数据类型 - -四种标量类型: - -- [boolean](http://php.net/manual/zh/language.types.boolean.php)(布尔型) -- [integer](http://php.net/manual/zh/language.types.integer.php)(整型) -- [float](http://php.net/manual/zh/language.types.float.php)(浮点型,也称作 [double](http://php.net/manual/zh/language.types.float.php)) -- [string](http://php.net/manual/zh/language.types.string.php)(字符串) - -三种复合类型: - -- [array](http://php.net/manual/zh/language.types.array.php)(数组) -- [object](http://php.net/manual/zh/language.types.object.php)(对象) -- [callable](http://php.net/manual/zh/language.types.callable.php)(可调用) - -最后是两种特殊类型: - -- [resource](http://php.net/manual/zh/language.types.resource.php)(资源) -- [NULL](http://php.net/manual/zh/language.types.null.php)(无类型) - - - -### 1.布尔类型 Boolean - -布尔类型只有true和false两个值 - -以下的值会认为是false。其他之外的都是true - -- [布尔](http://php.net/manual/zh/language.types.boolean.php)值 **FALSE** 本身 -- [整型](http://php.net/manual/zh/language.types.integer.php)值 0(零) -- [浮点型](http://php.net/manual/zh/language.types.float.php)值 0.0(零) -- 空[字符串](http://php.net/manual/zh/language.types.string.php),以及[字符串](http://php.net/manual/zh/language.types.string.php) "0" -- 不包括任何元素的[数组](http://php.net/manual/zh/language.types.array.php) -- 特殊类型 [NULL](http://php.net/manual/zh/language.types.null.php)(包括尚未赋值的变量) -- 从空标记生成的 [SimpleXML](http://php.net/manual/zh/ref.simplexml.php) 对象 - -```php -var_dump((bool)'false') //true #字符串false -var_dump((bool) false) //false #布尔值false -``` - -### 2. 整型int - -整型值可以使用十进制,十六进制,八进制或二进制表示,前面可以加上可选的符号(- 或者 +) - -自 PHP 4.4.0 和 PHP 5.0.5后,整型最大值可以用常量 **PHP_INT_MAX** 来表示,最小值可以在 PHP 7.0.0 及以后的版本中用常量 **PHP_INT_MIN**  - -```php -123 --123 -0123 #八进制 -0x123 #十六进制 -``` - -给定的一个数超出了 [integer](http://php.net/manual/zh/language.types.integer.php) 的范围,将会被解释为 [float](http://php.net/manual/zh/language.types.float.php)。同样如果执行的运算结果超出了 [integer](http://php.net/manual/zh/language.types.integer.php) 范围,也会返回 [float](http://php.net/manual/zh/language.types.float.php) - -从其他类型转换成整型 - -- 布尔值转整型 **false** 产生 0 **true** 产生 1 - -- 从浮点型转换 向下取整 0.5 转换成 0 - -- 字符串转整型 - - > 如果该字符串没有包含 '.','e' 或 'E' 并且其数字值在整型的范围之内(由 **PHP_INT_MAX** 所定义),该字符串将被当成 [integer](http://php.net/manual/zh/language.types.integer.php) 来取值。其它所有情况下都被作为 [float](http://php.net/manual/zh/language.types.float.php) 来取值。该字符串的开始部分决定了它的值。如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字(可能有小数点),再跟着可选的指数部分。指数部分由 'e' 或 'E' 后面跟着一个或多个数字构成 - -```php -bob3+1 //1 -3bob+1 //4 - -``` - -### 3. 浮点数float - -浮点数,也就是我们说的小数。由于浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16 - -**永远不要比较两个浮点数的值** - - - -### 4.字符串string - -字符串由一些字符组成。字符串有四种表示方法 - -- 单引号 - - 单引号是最简单的表示方法。在C语言中 单个字符用单引号表示。单引号的变量不解析.表达一个单引号自身,需在它的前面加个反斜线(*\*)来转义 - - ```php - $a = 'abc'; - $b = '$a';//$a - $c = "$a"; //abc - ``` - - ​ - -- 双引号 - - 双引号非转义任何其它字符都会导致反斜线被显示出来。\*\\{$var}* 中的反斜线还不会被显示出来。 - -- heredoc - - heredoc 结构是一种提供一个开始标记和一个结束标记。方便书写大段的字符串。结束标记必须**顶格写**。 - - 类似双引号。单引号不转义。变量解析 - - ```php - - ``` - - ​ - -- nowdoc - - Nowdoc 结构是类似于单引号字符串的。Nowdoc 结构很象 heredoc 结构,但是 nowdoc 中不进行解析操作 nowdoc前需要加上单引号。 - - ```php - value.通过数组可以实现丰富的数据类型,如字典、列表、集合、栈等。 - -定义数组 - -```php -$arr = array(1,2,3); -//简写 php5.4+ -$arr2 = [1,2,3]; -$arr2[1]; -$arr3 = ["key"=>"value"]; - -$arr3["key"]; -``` - -PHP 将自动使用之前用过的最大 INT键名加上 1 作为新的键名.PHP 实际并不区分索引数组和关联数组。 - - - -### 6. 对象object - -创建一个新的对象的时候,使用new 关键字 - -``` -class obj {} -$a = new obj(); -``` - - - -### 7.资源类型resource - -资源类型是保持外部的一个引用。如数据库的链接,文件的句柄等。 - -```php -$fp = fopen("./a.log");//resource - -``` - - - -### 8. NULL类型 - -当一个变量没有被赋值,那么该变量的值就是null。以下的情况是一个变量被认为是null - -- 被赋值成null -- 未赋值 -- 被unset - -```php -$a = null // null -$b;//var_dump($b); null -$c = 1; -unset($c);//var_dump($c); null -``` - -### 9. 回调 callback - -```php -function test(){ - echo "echo"; -} -call_user_function('test'); -``` - +## 基本语法 + +php的标记使用`` 也可以使用`` (需要打开短标签) 建议使用前者 + +> 文件末尾的 PHP 代码段结束标记可以不要,有些情况下当使用 [include](http://php.net/manual/zh/function.include.php) 或者 [require](http://php.net/manual/zh/function.require.php) 时省略掉会更好些,这样不期望的空白符就不会出现在文件末尾,之后仍然可以输出响应标头。在使用输出缓冲时也很便利,就不会看到由包含文件生成的不期望的空白符 + +```php + +``` + +## 数据类型 + +四种标量类型: + +- [boolean](http://php.net/manual/zh/language.types.boolean.php)(布尔型) +- [integer](http://php.net/manual/zh/language.types.integer.php)(整型) +- [float](http://php.net/manual/zh/language.types.float.php)(浮点型,也称作 [double](http://php.net/manual/zh/language.types.float.php)) +- [string](http://php.net/manual/zh/language.types.string.php)(字符串) + +三种复合类型: + +- [array](http://php.net/manual/zh/language.types.array.php)(数组) +- [object](http://php.net/manual/zh/language.types.object.php)(对象) +- [callable](http://php.net/manual/zh/language.types.callable.php)(可调用) + +最后是两种特殊类型: + +- [resource](http://php.net/manual/zh/language.types.resource.php)(资源) +- [NULL](http://php.net/manual/zh/language.types.null.php)(无类型) + + + +### 1.布尔类型 Boolean + +布尔类型只有true和false两个值 + +以下的值会认为是false。其他之外的都是true + +- [布尔](http://php.net/manual/zh/language.types.boolean.php)值 **FALSE** 本身 +- [整型](http://php.net/manual/zh/language.types.integer.php)值 0(零) +- [浮点型](http://php.net/manual/zh/language.types.float.php)值 0.0(零) +- 空[字符串](http://php.net/manual/zh/language.types.string.php),以及[字符串](http://php.net/manual/zh/language.types.string.php) "0" +- 不包括任何元素的[数组](http://php.net/manual/zh/language.types.array.php) +- 特殊类型 [NULL](http://php.net/manual/zh/language.types.null.php)(包括尚未赋值的变量) +- 从空标记生成的 [SimpleXML](http://php.net/manual/zh/ref.simplexml.php) 对象 + +```php +var_dump((bool)'false') //true #字符串false +var_dump((bool) false) //false #布尔值false +``` + +### 2. 整型int + +整型值可以使用十进制,十六进制,八进制或二进制表示,前面可以加上可选的符号(- 或者 +) + +自 PHP 4.4.0 和 PHP 5.0.5后,整型最大值可以用常量 **PHP_INT_MAX** 来表示,最小值可以在 PHP 7.0.0 及以后的版本中用常量 **PHP_INT_MIN**  + +```php +123 +-123 +0123 #八进制 +0x123 #十六进制 +``` + +给定的一个数超出了 [integer](http://php.net/manual/zh/language.types.integer.php) 的范围,将会被解释为 [float](http://php.net/manual/zh/language.types.float.php)。同样如果执行的运算结果超出了 [integer](http://php.net/manual/zh/language.types.integer.php) 范围,也会返回 [float](http://php.net/manual/zh/language.types.float.php) + +从其他类型转换成整型 + +- 布尔值转整型 **false** 产生 0 **true** 产生 1 + +- 从浮点型转换 向下取整 0.5 转换成 0 + +- 字符串转整型 + + > 如果该字符串没有包含 '.','e' 或 'E' 并且其数字值在整型的范围之内(由 **PHP_INT_MAX** 所定义),该字符串将被当成 [integer](http://php.net/manual/zh/language.types.integer.php) 来取值。其它所有情况下都被作为 [float](http://php.net/manual/zh/language.types.float.php) 来取值。该字符串的开始部分决定了它的值。如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字(可能有小数点),再跟着可选的指数部分。指数部分由 'e' 或 'E' 后面跟着一个或多个数字构成 + +```php +bob3+1 //1 +3bob+1 //4 + +``` + +### 3. 浮点数float + +浮点数,也就是我们说的小数。由于浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16 + +**永远不要比较两个浮点数的值** + + + +### 4.字符串string + +字符串由一些字符组成。字符串有四种表示方法 + +- 单引号 + + 单引号是最简单的表示方法。在C语言中 单个字符用单引号表示。单引号的变量不解析.表达一个单引号自身,需在它的前面加个反斜线(*\*)来转义 + + ```php + $a = 'abc'; + $b = '$a';//$a + $c = "$a"; //abc + ``` + + ​ + +- 双引号 + + 双引号非转义任何其它字符都会导致反斜线被显示出来。\*\\{$var}* 中的反斜线还不会被显示出来。 + +- heredoc + + heredoc 结构是一种提供一个开始标记和一个结束标记。方便书写大段的字符串。结束标记必须**顶格写**。 + + 类似双引号。单引号不转义。变量解析 + + ```php + + ``` + + ​ + +- nowdoc + + Nowdoc 结构是类似于单引号字符串的。Nowdoc 结构很象 heredoc 结构,但是 nowdoc 中不进行解析操作 nowdoc前需要加上单引号。 + + ```php + value.通过数组可以实现丰富的数据类型,如字典、列表、集合、栈等。 + +定义数组 + +```php +$arr = array(1,2,3); +//简写 php5.4+ +$arr2 = [1,2,3]; +$arr2[1]; +$arr3 = ["key"=>"value"]; + +$arr3["key"]; +``` + +PHP 将自动使用之前用过的最大 INT键名加上 1 作为新的键名.PHP 实际并不区分索引数组和关联数组。 + + + +### 6. 对象object + +创建一个新的对象的时候,使用new 关键字 + +``` +class obj {} +$a = new obj(); +``` + + + +### 7.资源类型resource + +资源类型是保持外部的一个引用。如数据库的链接,文件的句柄等。 + +```php +$fp = fopen("./a.log");//resource + +``` + + + +### 8. NULL类型 + +当一个变量没有被赋值,那么该变量的值就是null。以下的情况是一个变量被认为是null + +- 被赋值成null +- 未赋值 +- 被unset + +```php +$a = null // null +$b;//var_dump($b); null +$c = 1; +unset($c);//var_dump($c); null +``` + +### 9. 回调 callback + +```php +function test(){ + echo "echo"; +} +call_user_function('test'); +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/10.\351\224\231\350\257\257\345\222\214\345\274\202\345\270\270\345\244\204\347\220\206.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/10.\351\224\231\350\257\257\345\222\214\345\274\202\345\270\270\345\244\204\347\220\206.md" index b798979..14eccfd 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/10.\351\224\231\350\257\257\345\222\214\345\274\202\345\270\270\345\244\204\347\220\206.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/10.\351\224\231\350\257\257\345\222\214\345\274\202\345\270\270\345\244\204\347\220\206.md" @@ -1,107 +1,107 @@ -## 错误和异常 - -在编码的时候,我们无时无刻会遇到错误和异常,所以我们需要处理这些错误。 - -php的错误类型有 - -- E_ERROR 致命的错误。会中断程序的执行 -- E_WARNING 警告。不会中断程序 -- E_NOTICE 通知,运行时通知。表示脚本遇到可能会表现为错误的情况 -- E_PARSE 解析错误,一般是语法错误。 -- E_STRICT PHP 对代码的修改建议 -- E_DEPRECATED 将会对在未来版本中可能无法正常工作的代码给出警告 - -### 错误 - -在开发模式中,我们一般需要打开**error_reporting** 设置为**E_ALL**。然后把**display_errors** 设置为on - -如果需要记录错误日志,则需要配置log_errors. - -开发者可以 通过set_error_handle()自己接管错误。 - -```php - -set_error_handle(function($errno,$errstr,$errfile,$errline){ - -}) - -/** -* throw exceptions based on E_* error types -*/ -set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context) -{ - // error was suppressed with the @-operator - if (0 === error_reporting()) { return false;} - switch($err_severity) - { - case E_ERROR: throw new ErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_WARNING: throw new WarningException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_PARSE: throw new ParseException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_NOTICE: throw new NoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_CORE_ERROR: throw new CoreErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_CORE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_COMPILE_ERROR: throw new CompileErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_COMPILE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_USER_ERROR: throw new UserErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_USER_WARNING: throw new UserWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_USER_NOTICE: throw new UserNoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_STRICT: throw new StrictException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_RECOVERABLE_ERROR: throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_DEPRECATED: throw new DeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); - case E_USER_DEPRECATED: throw new UserDeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); - } -}); - -class WarningException extends ErrorException {} -class ParseException extends ErrorException {} -class NoticeException extends ErrorException {} -class CoreErrorException extends ErrorException {} -class CoreWarningException extends ErrorException {} -class CompileErrorException extends ErrorException {} -class CompileWarningException extends ErrorException {} -class UserErrorException extends ErrorException {} -class UserWarningException extends ErrorException {} -class UserNoticeException extends ErrorException {} -class StrictException extends ErrorException {} -class RecoverableErrorException extends ErrorException {} -class DeprecatedException extends ErrorException {} -class UserDeprecatedException extends ErrorException {} -``` - -#### PHP7的错误处理 - -PHP 7 改变了大多数错误的报告方式。error可以通过exception异常进行捕获到.不能通过try catch捕获。但是可以通过注册到set_exception_handle捕获。 - -- Throwable - - Error - - Exception - -```php -try -{ - // Code that may throw an Exception or Error. -} -catch (Throwable $t) -{ - // Executed only in PHP 7, will not match in PHP 5 -} -catch (Exception $e) -{ - // Executed only in PHP 5, will not be reached in PHP 7 -} -``` - -### 异常 - -捕获异常可以通过try catch 语句 - -```php -try{ - //异常的代码 -}catch (Exception $e){ - //处理异常 -}finally{ - //最后执行的 -} -``` - +## 错误和异常 + +在编码的时候,我们无时无刻会遇到错误和异常,所以我们需要处理这些错误。 + +php的错误类型有 + +- E_ERROR 致命的错误。会中断程序的执行 +- E_WARNING 警告。不会中断程序 +- E_NOTICE 通知,运行时通知。表示脚本遇到可能会表现为错误的情况 +- E_PARSE 解析错误,一般是语法错误。 +- E_STRICT PHP 对代码的修改建议 +- E_DEPRECATED 将会对在未来版本中可能无法正常工作的代码给出警告 + +### 错误 + +在开发模式中,我们一般需要打开**error_reporting** 设置为**E_ALL**。然后把**display_errors** 设置为on + +如果需要记录错误日志,则需要配置log_errors. + +开发者可以 通过set_error_handle()自己接管错误。 + +```php + +set_error_handle(function($errno,$errstr,$errfile,$errline){ + +}) + +/** +* throw exceptions based on E_* error types +*/ +set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context) +{ + // error was suppressed with the @-operator + if (0 === error_reporting()) { return false;} + switch($err_severity) + { + case E_ERROR: throw new ErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_WARNING: throw new WarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_PARSE: throw new ParseException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_NOTICE: throw new NoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_CORE_ERROR: throw new CoreErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_CORE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_COMPILE_ERROR: throw new CompileErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_COMPILE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_ERROR: throw new UserErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_WARNING: throw new UserWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_NOTICE: throw new UserNoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_STRICT: throw new StrictException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_RECOVERABLE_ERROR: throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_DEPRECATED: throw new DeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_DEPRECATED: throw new UserDeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); + } +}); + +class WarningException extends ErrorException {} +class ParseException extends ErrorException {} +class NoticeException extends ErrorException {} +class CoreErrorException extends ErrorException {} +class CoreWarningException extends ErrorException {} +class CompileErrorException extends ErrorException {} +class CompileWarningException extends ErrorException {} +class UserErrorException extends ErrorException {} +class UserWarningException extends ErrorException {} +class UserNoticeException extends ErrorException {} +class StrictException extends ErrorException {} +class RecoverableErrorException extends ErrorException {} +class DeprecatedException extends ErrorException {} +class UserDeprecatedException extends ErrorException {} +``` + +#### PHP7的错误处理 + +PHP 7 改变了大多数错误的报告方式。error可以通过exception异常进行捕获到.不能通过try catch捕获。但是可以通过注册到set_exception_handle捕获。 + +- Throwable + - Error + - Exception + +```php +try +{ + // Code that may throw an Exception or Error. +} +catch (Throwable $t) +{ + // Executed only in PHP 7, will not match in PHP 5 +} +catch (Exception $e) +{ + // Executed only in PHP 5, will not be reached in PHP 7 +} +``` + +### 异常 + +捕获异常可以通过try catch 语句 + +```php +try{ + //异常的代码 +}catch (Exception $e){ + //处理异常 +}finally{ + //最后执行的 +} +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/2-\345\217\230\351\207\217\345\222\214\345\270\270\351\207\217.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/2-\345\217\230\351\207\217\345\222\214\345\270\270\351\207\217.md" index 87c0b4b..ebabe13 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/2-\345\217\230\351\207\217\345\222\214\345\270\270\351\207\217.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/2-\345\217\230\351\207\217\345\222\214\345\270\270\351\207\217.md" @@ -1,92 +1,92 @@ -## 变量 - -php中的变量是以 `$` 开头的。变量名区分大小写。合法的变量名是字母或者下划线开头,后面跟着任意数量的字母,数字,或者下划线 - -在此所说的字母是 a-z,A-Z,以及 ASCII 字符从 127 到 255(*0x7f-0xff*) - -变量默认总是传值赋值 - -```php -$a $_a $张三 // 合法的 -$2aaa //非法的 -$a = 123; -``` - - - -### 变量的作用域 - -在最外层定义的是全局变量。在全局都有效。在函数内部的成为局部变量。只有函数内部可以访问。使用static修饰的变量,是静态变量。静态变量不会被销毁。只有等程序运行结束后才被回收. - -**$this 是一个特殊的变量。不能定位成this变量** - -```php - -``` - - - -确定变量的类型。php提供了以下函数 - -```php -gettype() is_bool() is_string() is_object() is_array(); -``` - -## 常量 - -常量一般是定义之后不能再更改的变量。传统上使用大写字母来定义常量.合法的常量命名跟变量一致。只是不需要加 `$` 符号。 - -```php -IS_DEBUG; -define("IS_DEBUG",0); -``` - -#### 魔术常量 - -魔术常量是php内置的。可能跟着代码的变化而变化。一般魔术常量 以双下划线开始 双下划线结束。 - -```php -__LINE__ -__DIR__ -__FILE__ -__CLASS__ -``` - - - -## 表达式 - -最精确的定义一个表达式的方式就是“任何有值的东西”。最简单的表达式。常量和变量。 - -```php -$a; -;// 空表达式 -$a = 1;//赋值表达式 - -function f(){} //函数表达式 -$a>1 //比较表达式 - -``` - +## 变量 + +php中的变量是以 `$` 开头的。变量名区分大小写。合法的变量名是字母或者下划线开头,后面跟着任意数量的字母,数字,或者下划线 + +在此所说的字母是 a-z,A-Z,以及 ASCII 字符从 127 到 255(*0x7f-0xff*) + +变量默认总是传值赋值 + +```php +$a $_a $张三 // 合法的 +$2aaa //非法的 +$a = 123; +``` + + + +### 变量的作用域 + +在最外层定义的是全局变量。在全局都有效。在函数内部的成为局部变量。只有函数内部可以访问。使用static修饰的变量,是静态变量。静态变量不会被销毁。只有等程序运行结束后才被回收. + +**$this 是一个特殊的变量。不能定位成this变量** + +```php + +``` + + + +确定变量的类型。php提供了以下函数 + +```php +gettype() is_bool() is_string() is_object() is_array(); +``` + +## 常量 + +常量一般是定义之后不能再更改的变量。传统上使用大写字母来定义常量.合法的常量命名跟变量一致。只是不需要加 `$` 符号。 + +```php +IS_DEBUG; +define("IS_DEBUG",0); +``` + +#### 魔术常量 + +魔术常量是php内置的。可能跟着代码的变化而变化。一般魔术常量 以双下划线开始 双下划线结束。 + +```php +__LINE__ +__DIR__ +__FILE__ +__CLASS__ +``` + + + +## 表达式 + +最精确的定义一个表达式的方式就是“任何有值的东西”。最简单的表达式。常量和变量。 + +```php +$a; +;// 空表达式 +$a = 1;//赋值表达式 + +function f(){} //函数表达式 +$a>1 //比较表达式 + +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/3-\350\277\220\347\256\227\347\254\246.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/3-\350\277\220\347\256\227\347\254\246.md" index 87a8ddd..08c673b 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/3-\350\277\220\347\256\227\347\254\246.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/3-\350\277\220\347\256\227\347\254\246.md" @@ -1,159 +1,159 @@ -## 运算符 - -运算符就是通过一个或者多个符号产生另外一个结果的符号。常见的有加减乘除等 - -### 运算符优先级 - -运算符是有优先级的,优先级的先结合。 - -| 结合方向 | 运算符 | 附加信息 | -| ----- | ---------------------------------------- | ---------------------------------------- | -| 无 | clone new | [clone](http://php.net/manual/zh/language.oop5.cloning.php) 和 [new](http://php.net/manual/zh/language.oop5.basic.php#language.oop5.basic.new) | -| 左 | *[* | [array()](http://php.net/manual/zh/function.array.php) | -| 右 | **\** | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php) | -| 右 | *++* *--* *~* *(int)* *(float)* *(string)* *(array)* *(object)* *(bool)* *@* | [类型](http://php.net/manual/zh/language.types.php)和[递增/递减](http://php.net/manual/zh/language.operators.increment.php) | -| 无 | *instanceof* | [类型](http://php.net/manual/zh/language.types.php) | -| 右 | *!* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | -| 左 | *** */* *%* | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php) | -| 左 | *+* *-* *.* | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php)和[字符串运算符](http://php.net/manual/zh/language.operators.string.php) | -| 左 | *<<* *>>* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | -| 无 | *<* *<=* *>* *>=* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | -| 无 | *==* *!=* *===* *!==* *<>* *<=>* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | -| 左 | *&* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php)和[引用](http://php.net/manual/zh/language.references.php) | -| 左 | *^* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | -| 左 | *\|* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | -| 左 | *&&* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | -| 左 | *\|\|* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | -| 左 | *??* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | -| 左 | *? :* | [ternary](http://php.net/manual/zh/language.operators.comparison.php#language.operators.comparison.ternary) | -| right | *=* *+=* *-=* **=* **\*=* */=* *.=* *%=* *&=* *\|=* *^=* *<<=* *>>=* | [赋值运算符](http://php.net/manual/zh/language.operators.assignment.php) | -| 左 | *and* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | -| 左 | *xor* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | -| 左 | *or* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | - - - -```php -3+2*4// 11 -5/3 // 1.33333 -5%2 //2 -``` - -- 除法运算符总是返回浮点数. -- 取模运算符的操作数在运算之前都会转换成整数(除去小数部分) - -### 递增、递减 - -| ++$a | 前加 | $a 的值加一,然后返回 $a。 | -| ---- | ---- | ------------------ | -| $a++ | 后加 | 返回 $a,然后将 $a 的值加一。 | -| --$a | 前减 | $a 的值减一, 然后返回 $a。 | -| $a-- | 后减 | 返回 $a,然后将 $a 的值减一。 | - -```php -$a = 1; - -echo $a++;//1 -$b = 1; -echo ++$b; - -$c = 'A'; -echo ++$c;//B - -$bool = false; -$bool++; - -var_dump($bool); //false; -``` - -- 处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的 -- 字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z) -- **递增或递减布尔值没有效果。** - -### 位运算 - -| **\$a \&\ $b** | And(按位与) | 将把 $a 和 $b 中都为 1 的位设为 1。 | -| -------------- | --------------- | ---------------------------------- | -| **$a \|\$b** | Or(按位或) | 将把 $a 和 $b 中任何一个为 1 的位设为 1。 | -| **$a ^ \$b** | Xor(按位异或) | 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。 | -| **~ \$a** | Not(按位取反) | 将 $a 中为 0 的位设为 1,反之亦然。 | -| **\$a << \$b** | Shift left(左移) | 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。 | -| **\$a >> \$b** | Shift right(右移) | 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。 | - - - -### 比较运算符 - -| $a == $b | 等于 | **TRUE**,如果类型转换后 $a 等于 $b。 | -| --------------- | :-----------: | :--------------------------------------- | -| $a === $b | 全等 | **TRUE**,如果 $a 等于 $b,并且它们的类型也相同。 | -| $a != $b | 不等 | **TRUE**,如果类型转换后 $a 不等于 $b。 | -| $a <> $b | 不等 | **TRUE**,如果类型转换后 $a 不等于 $b。 | -| $a !== $b | 不全等 | **TRUE**,如果 $a 不等于 $b,或者它们的类型不同。 | -| $a < $b | 小与 | **TRUE**,如果 $a 严格小于 $b。 | -| $a > $b | 大于 | **TRUE**,如果 $a 严格大于 $b。 | -| $a <= $b | 小于等于 | **TRUE**,如果 $a 小于或者等于 $b。 | -| $a >= $b | 大于等于 | **TRUE**,如果 $a 大于或者等于 $b。 | -| $a <=> $b | 太空船运算符(组合比较符) | 当$a小于、等于、大于$b时 分别返回一个小于、等于、大于0的[integer](http://php.net/manual/zh/language.types.integer.php) 值。 PHP7开始提供. | -| $a ?? $b ?? \$c | NULL 合并操作符 | 从左往右第一个存在且不为 **NULL** 的操作数。如果都没有定义且不为 **NULL**,则返回 **NULL**。PHP7开始提供。 | - -如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被[转换为**数值**]并且比较按照数值来进行. - -由于浮点数 **float**的内部表达方式,不应比较两个浮点数**float**是否相等。 - -```php -$a = 'abc'; -$a == 1;//false -$a = 0; -$b = 'abc'; - -$a == $b;//true; -``` - -### 执行运算符 - -PHP 支持一个执行运算符:反引号(``)。注意这不是单引号!PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回 - -```php -`ls /home/` -``` - - - -### 逻辑运算符 - -| \$a and $b | And(逻辑与) | **TRUE**,如果 $a 和 $b 都为 **TRUE**。 | -| ------------ | --------- | --------------------------------------- | -| \$a or \$b | Or(逻辑或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**。 | -| \$a xor \$b | Xor(逻辑异或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**,但不同时是。 | -| ! $a | Not(逻辑非) | **TRUE**,如果 $a 不为 **TRUE**。 | -| \$a && $b | And(逻辑与) | **TRUE**,如果 $a 和 $b 都为 **TRUE**。 | -| \$a \|\| \$b | Or(逻辑或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**。 | - -```php -$a = (true || false); -``` - -### 字符串连接符 - -字符串连接符号 点(.) - -```php -$a = 1; -$b = 222 -echo $a . $b; //1222 - -``` - - - -### 类型运算符 - -*instanceof* 用于确定一个 PHP 变量是否属于某一类 - -```php -$c = new C(); - -$c instanceof C; -``` - +## 运算符 + +运算符就是通过一个或者多个符号产生另外一个结果的符号。常见的有加减乘除等 + +### 运算符优先级 + +运算符是有优先级的,优先级的先结合。 + +| 结合方向 | 运算符 | 附加信息 | +| ----- | ---------------------------------------- | ---------------------------------------- | +| 无 | clone new | [clone](http://php.net/manual/zh/language.oop5.cloning.php) 和 [new](http://php.net/manual/zh/language.oop5.basic.php#language.oop5.basic.new) | +| 左 | *[* | [array()](http://php.net/manual/zh/function.array.php) | +| 右 | **\** | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php) | +| 右 | *++* *--* *~* *(int)* *(float)* *(string)* *(array)* *(object)* *(bool)* *@* | [类型](http://php.net/manual/zh/language.types.php)和[递增/递减](http://php.net/manual/zh/language.operators.increment.php) | +| 无 | *instanceof* | [类型](http://php.net/manual/zh/language.types.php) | +| 右 | *!* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | +| 左 | *** */* *%* | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php) | +| 左 | *+* *-* *.* | [算术运算符](http://php.net/manual/zh/language.operators.arithmetic.php)和[字符串运算符](http://php.net/manual/zh/language.operators.string.php) | +| 左 | *<<* *>>* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | +| 无 | *<* *<=* *>* *>=* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | +| 无 | *==* *!=* *===* *!==* *<>* *<=>* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | +| 左 | *&* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php)和[引用](http://php.net/manual/zh/language.references.php) | +| 左 | *^* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | +| 左 | *\|* | [位运算符](http://php.net/manual/zh/language.operators.bitwise.php) | +| 左 | *&&* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | +| 左 | *\|\|* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | +| 左 | *??* | [比较运算符](http://php.net/manual/zh/language.operators.comparison.php) | +| 左 | *? :* | [ternary](http://php.net/manual/zh/language.operators.comparison.php#language.operators.comparison.ternary) | +| right | *=* *+=* *-=* **=* **\*=* */=* *.=* *%=* *&=* *\|=* *^=* *<<=* *>>=* | [赋值运算符](http://php.net/manual/zh/language.operators.assignment.php) | +| 左 | *and* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | +| 左 | *xor* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | +| 左 | *or* | [逻辑运算符](http://php.net/manual/zh/language.operators.logical.php) | + + + +```php +3+2*4// 11 +5/3 // 1.33333 +5%2 //2 +``` + +- 除法运算符总是返回浮点数. +- 取模运算符的操作数在运算之前都会转换成整数(除去小数部分) + +### 递增、递减 + +| ++$a | 前加 | $a 的值加一,然后返回 $a。 | +| ---- | ---- | ------------------ | +| $a++ | 后加 | 返回 $a,然后将 $a 的值加一。 | +| --$a | 前减 | $a 的值减一, 然后返回 $a。 | +| $a-- | 后减 | 返回 $a,然后将 $a 的值减一。 | + +```php +$a = 1; + +echo $a++;//1 +$b = 1; +echo ++$b; + +$c = 'A'; +echo ++$c;//B + +$bool = false; +$bool++; + +var_dump($bool); //false; +``` + +- 处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的 +- 字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z) +- **递增或递减布尔值没有效果。** + +### 位运算 + +| **\$a \&\ $b** | And(按位与) | 将把 $a 和 $b 中都为 1 的位设为 1。 | +| -------------- | --------------- | ---------------------------------- | +| **$a \|\$b** | Or(按位或) | 将把 $a 和 $b 中任何一个为 1 的位设为 1。 | +| **$a ^ \$b** | Xor(按位异或) | 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。 | +| **~ \$a** | Not(按位取反) | 将 $a 中为 0 的位设为 1,反之亦然。 | +| **\$a << \$b** | Shift left(左移) | 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。 | +| **\$a >> \$b** | Shift right(右移) | 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。 | + + + +### 比较运算符 + +| $a == $b | 等于 | **TRUE**,如果类型转换后 $a 等于 $b。 | +| --------------- | :-----------: | :--------------------------------------- | +| $a === $b | 全等 | **TRUE**,如果 $a 等于 $b,并且它们的类型也相同。 | +| $a != $b | 不等 | **TRUE**,如果类型转换后 $a 不等于 $b。 | +| $a <> $b | 不等 | **TRUE**,如果类型转换后 $a 不等于 $b。 | +| $a !== $b | 不全等 | **TRUE**,如果 $a 不等于 $b,或者它们的类型不同。 | +| $a < $b | 小与 | **TRUE**,如果 $a 严格小于 $b。 | +| $a > $b | 大于 | **TRUE**,如果 $a 严格大于 $b。 | +| $a <= $b | 小于等于 | **TRUE**,如果 $a 小于或者等于 $b。 | +| $a >= $b | 大于等于 | **TRUE**,如果 $a 大于或者等于 $b。 | +| $a <=> $b | 太空船运算符(组合比较符) | 当$a小于、等于、大于$b时 分别返回一个小于、等于、大于0的[integer](http://php.net/manual/zh/language.types.integer.php) 值。 PHP7开始提供. | +| $a ?? $b ?? \$c | NULL 合并操作符 | 从左往右第一个存在且不为 **NULL** 的操作数。如果都没有定义且不为 **NULL**,则返回 **NULL**。PHP7开始提供。 | + +如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被[转换为**数值**]并且比较按照数值来进行. + +由于浮点数 **float**的内部表达方式,不应比较两个浮点数**float**是否相等。 + +```php +$a = 'abc'; +$a == 1;//false +$a = 0; +$b = 'abc'; + +$a == $b;//true; +``` + +### 执行运算符 + +PHP 支持一个执行运算符:反引号(``)。注意这不是单引号!PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回 + +```php +`ls /home/` +``` + + + +### 逻辑运算符 + +| \$a and $b | And(逻辑与) | **TRUE**,如果 $a 和 $b 都为 **TRUE**。 | +| ------------ | --------- | --------------------------------------- | +| \$a or \$b | Or(逻辑或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**。 | +| \$a xor \$b | Xor(逻辑异或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**,但不同时是。 | +| ! $a | Not(逻辑非) | **TRUE**,如果 $a 不为 **TRUE**。 | +| \$a && $b | And(逻辑与) | **TRUE**,如果 $a 和 $b 都为 **TRUE**。 | +| \$a \|\| \$b | Or(逻辑或) | **TRUE**,如果 $a 或 $b 任一为 **TRUE**。 | + +```php +$a = (true || false); +``` + +### 字符串连接符 + +字符串连接符号 点(.) + +```php +$a = 1; +$b = 222 +echo $a . $b; //1222 + +``` + + + +### 类型运算符 + +*instanceof* 用于确定一个 PHP 变量是否属于某一类 + +```php +$c = new C(); + +$c instanceof C; +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/4.\346\265\201\347\250\213\346\216\247\345\210\266.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/4.\346\265\201\347\250\213\346\216\247\345\210\266.md" index c6951cb..a7f9287 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/4.\346\265\201\347\250\213\346\216\247\345\210\266.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/4.\346\265\201\347\250\213\346\216\247\345\210\266.md" @@ -1,272 +1,272 @@ -### if else - -```php -if($a) { - -} - -if($a == 1) { - -} else{ - -} - -if($a == 1) { - echo '1'; -}else if($a == 2) { - echo '2'; -}else{ - echo 'other'; -} - -#简写 不推荐 -if ($a == 1): - echo "1"; -else: - echo "else"; -endif; -``` - -### switch - -*switch* 语句类似于具有同一个表达式的一系列 *if* 语句。很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码.**每个case分支的break不能省去,如果省去,则会往下执行** - -```php -if($a == 1) { - echo '1'; -}else if($a == 2) { - echo '2'; -}else{ - echo 'other'; -} -# swtich -switch ($a) { - case 1: - echo '1'; - break; - case 2: - echo '2'; - break; - default: - echo 'othre'; - break; -} -``` - - - -### while 、do while - -*while* 循环是 PHP 中最简单的循环类型.只要表达式是true是一直执行,直到条件为false为止. - -*do-while* 循环和 *while* 循环非常相似,区别在于表达式的值是在每次循环结束时检查而不是开始时。和一般的 *while* 循环主要的区别是 *do-while*的循环语句保证会执行一次(表达式的真值在每次循环结束后检查) - -```php -$a = 10; -while($a>1) { - echo $a--;//1098765432 -} -#do while -$b = 1; -do{ - echo $b; //1 -}while($b>1) -``` - - - -### for、foreach - -*for* 循环是 PHP 中最复杂的循环结构。它的行为和 C 语言的相似 - -``` -for (expr1; expr2; expr3) - statement - -``` - -第一个表达式(expr1)在循环开始前无条件求值(并执行)一次。 - -expr2 在每次循环开始前求值。如果值为 **TRUE**,则继续循环,执行嵌套的循环语句。如果值为 **FALSE**,则终止循环。 - -expr3 在每次循环之后被求值(并执行)。 - -```php -$people = Array( - Array('name' => 'Kalle', 'salt' => 856412), - Array('name' => 'Pierre', 'salt' => 215863) - ); - -for($i = 0; $i < count($people); ++$i) -{ - $people[$i]['salt'] = rand(000000, 999999); -} -//每次都计算count 代码执行慢 可以使用中间变量 - -$count = count($people); -for($i = 0; $i < $count; ++$i) -{ - $people[$i]['salt'] = rand(000000, 999999); -} -``` - -*foreach* 语法结构提供了遍历数组的简单方式。*foreach* 仅能够应用于数组和对象 - -```php -foreach($array as $key=>$value) { - -} -# 简写 -foreach($array as $key=>$value): - -endforeach; -#引用修改 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。 - -foreach (array(1, 2, 3, 4) as &$value) { - $value = $value * 2; -} -unset($value); -``` - -### break 、continue - -*break* 结束当前 *for*,*foreach*,*while*,*do-while* 或者 *switch* 结构的执行。break后面可以跟一个数字。表示跳出几重循环 - -```php -for($i = 0; $i < 10; $i++) { - if($i==5){ - break; - } - echo $i; -} -//01234 -for($i=0;$i<10;$i++) { - for($j=0;$j<5;$j++) { - if($j == 2) { - break 2; #跳出两层循环 - } - } -} -``` - -***continue*** 在循环结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次循环 - -***continue*** 接受一个可选的数字参数来决定跳过几重循环到循环结尾。默认值是 *1*,即跳到当前循环末尾 - -```php -for($i = 0; $i < 10; $i++) { - if($i==5){ - continue; - } - echo $i; -} -// 012346789 -``` - - - -### declare - -*declare* 结构用来设定一段代码的执行指令。*declare* 的语法和其它流程控制结构相似 - -目前只认识两个指令:*ticks* encoding - -Tick(时钟周期)是一个在 *declare* 代码段中解释器每执行 N 条可计时的低级语句就会发生的事件。N 的值是在 *declare* 中的 *directive* 部分用`ticks=N` 来指定的 - - encoding 指令来对每段脚本指定其编码方式。 - -```php -declare(ticks=1); - -// A function called on each tick event -function tick_handler() -{ - echo "tick_handler() called\n"; -} - -register_tick_function('tick_handler'); - -$a = 1; - -if ($a > 0) { - $a += 2; - print($a); -} -declare(encoding='ISO-8859-1'); -``` - - - -### return - -在函数中使用return 将结束函数的执行。 - - **return** 是语言结构而不是函数,因此其参数没有必要用括号将其括起来。通常都不用括号,实际上也应该不用,这样可以降低 PHP 的负担。 - -```php -function f(){ - return 1; - echo '11';//不会执行 -} - -#a.php - - -#b.php - -// ba -``` - -### include 、require - -include(path) 会按照给定的参数 进行查找,如果没有找到就到include_path中查找。如果还没有找到,那么就会抛出一个警告。 - -如果定义了路径——不管是绝对路径(在 Windows 下以盘符或者 *\* 开头,在 Unix/Linux 下以 */* 开头)还是当前目录的相对路径。include_path就会被忽略。 - -require 和include查找文件基本上一致。只是require会抛出一个error错误终止代码的执行。 - -require 和include是一个语言构造器而不是一个函数 - -```php -include 'a.php'; -echo "hello"; -# 会输出hello - -require 'a.php'; -echo "hello"; -# 抛出一个error hello不会输出。 -``` - -### include_once 、require_once - -*include_once* 、require_once 语句在脚本执行期间包含并运行指定文件.如果该文件中已经被包含过,则不会再次包含 - -include_once 会抛出warning 。require_once 会抛出error - - - -### goto - -goto操作符并不常用。用来跳转程序的一个位置。目标位置只能位于同一个文件和作用域 - -无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中 - -```php - -``` - +### if else + +```php +if($a) { + +} + +if($a == 1) { + +} else{ + +} + +if($a == 1) { + echo '1'; +}else if($a == 2) { + echo '2'; +}else{ + echo 'other'; +} + +#简写 不推荐 +if ($a == 1): + echo "1"; +else: + echo "else"; +endif; +``` + +### switch + +*switch* 语句类似于具有同一个表达式的一系列 *if* 语句。很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码.**每个case分支的break不能省去,如果省去,则会往下执行** + +```php +if($a == 1) { + echo '1'; +}else if($a == 2) { + echo '2'; +}else{ + echo 'other'; +} +# swtich +switch ($a) { + case 1: + echo '1'; + break; + case 2: + echo '2'; + break; + default: + echo 'othre'; + break; +} +``` + + + +### while 、do while + +*while* 循环是 PHP 中最简单的循环类型.只要表达式是true是一直执行,直到条件为false为止. + +*do-while* 循环和 *while* 循环非常相似,区别在于表达式的值是在每次循环结束时检查而不是开始时。和一般的 *while* 循环主要的区别是 *do-while*的循环语句保证会执行一次(表达式的真值在每次循环结束后检查) + +```php +$a = 10; +while($a>1) { + echo $a--;//1098765432 +} +#do while +$b = 1; +do{ + echo $b; //1 +}while($b>1) +``` + + + +### for、foreach + +*for* 循环是 PHP 中最复杂的循环结构。它的行为和 C 语言的相似 + +``` +for (expr1; expr2; expr3) + statement + +``` + +第一个表达式(expr1)在循环开始前无条件求值(并执行)一次。 + +expr2 在每次循环开始前求值。如果值为 **TRUE**,则继续循环,执行嵌套的循环语句。如果值为 **FALSE**,则终止循环。 + +expr3 在每次循环之后被求值(并执行)。 + +```php +$people = Array( + Array('name' => 'Kalle', 'salt' => 856412), + Array('name' => 'Pierre', 'salt' => 215863) + ); + +for($i = 0; $i < count($people); ++$i) +{ + $people[$i]['salt'] = rand(000000, 999999); +} +//每次都计算count 代码执行慢 可以使用中间变量 + +$count = count($people); +for($i = 0; $i < $count; ++$i) +{ + $people[$i]['salt'] = rand(000000, 999999); +} +``` + +*foreach* 语法结构提供了遍历数组的简单方式。*foreach* 仅能够应用于数组和对象 + +```php +foreach($array as $key=>$value) { + +} +# 简写 +foreach($array as $key=>$value): + +endforeach; +#引用修改 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。 + +foreach (array(1, 2, 3, 4) as &$value) { + $value = $value * 2; +} +unset($value); +``` + +### break 、continue + +*break* 结束当前 *for*,*foreach*,*while*,*do-while* 或者 *switch* 结构的执行。break后面可以跟一个数字。表示跳出几重循环 + +```php +for($i = 0; $i < 10; $i++) { + if($i==5){ + break; + } + echo $i; +} +//01234 +for($i=0;$i<10;$i++) { + for($j=0;$j<5;$j++) { + if($j == 2) { + break 2; #跳出两层循环 + } + } +} +``` + +***continue*** 在循环结构用用来跳过本次循环中剩余的代码并在条件求值为真时开始执行下一次循环 + +***continue*** 接受一个可选的数字参数来决定跳过几重循环到循环结尾。默认值是 *1*,即跳到当前循环末尾 + +```php +for($i = 0; $i < 10; $i++) { + if($i==5){ + continue; + } + echo $i; +} +// 012346789 +``` + + + +### declare + +*declare* 结构用来设定一段代码的执行指令。*declare* 的语法和其它流程控制结构相似 + +目前只认识两个指令:*ticks* encoding + +Tick(时钟周期)是一个在 *declare* 代码段中解释器每执行 N 条可计时的低级语句就会发生的事件。N 的值是在 *declare* 中的 *directive* 部分用`ticks=N` 来指定的 + + encoding 指令来对每段脚本指定其编码方式。 + +```php +declare(ticks=1); + +// A function called on each tick event +function tick_handler() +{ + echo "tick_handler() called\n"; +} + +register_tick_function('tick_handler'); + +$a = 1; + +if ($a > 0) { + $a += 2; + print($a); +} +declare(encoding='ISO-8859-1'); +``` + + + +### return + +在函数中使用return 将结束函数的执行。 + + **return** 是语言结构而不是函数,因此其参数没有必要用括号将其括起来。通常都不用括号,实际上也应该不用,这样可以降低 PHP 的负担。 + +```php +function f(){ + return 1; + echo '11';//不会执行 +} + +#a.php + + +#b.php + +// ba +``` + +### include 、require + +include(path) 会按照给定的参数 进行查找,如果没有找到就到include_path中查找。如果还没有找到,那么就会抛出一个警告。 + +如果定义了路径——不管是绝对路径(在 Windows 下以盘符或者 *\* 开头,在 Unix/Linux 下以 */* 开头)还是当前目录的相对路径。include_path就会被忽略。 + +require 和include查找文件基本上一致。只是require会抛出一个error错误终止代码的执行。 + +require 和include是一个语言构造器而不是一个函数 + +```php +include 'a.php'; +echo "hello"; +# 会输出hello + +require 'a.php'; +echo "hello"; +# 抛出一个error hello不会输出。 +``` + +### include_once 、require_once + +*include_once* 、require_once 语句在脚本执行期间包含并运行指定文件.如果该文件中已经被包含过,则不会再次包含 + +include_once 会抛出warning 。require_once 会抛出error + + + +### goto + +goto操作符并不常用。用来跳转程序的一个位置。目标位置只能位于同一个文件和作用域 + +无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中 + +```php + +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/5.\345\207\275\346\225\260.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/5.\345\207\275\346\225\260.md" index 35622d5..9fbd8ad 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/5.\345\207\275\346\225\260.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/5.\345\207\275\346\225\260.md" @@ -1,154 +1,154 @@ -## 函数 - -函数是将特定的代码放到一个区间里,方便下次的调用。 - -函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。 - -PHP 中的所有函数和类都具有全局作用域。PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。 - -函数名是大小写无关的 - -要避免递归函数/方法调用超过 100-200 层,因为可能会使堆栈崩溃从而使当前脚本终止 - -```php -function 函数名字(参数){ - //函数体 -} -``` - -### 函数参数 - -可以通过参数传递外部的信息到函数内部。 - -支持引用传递参数、默认参数 - -```php -$a = 100; -function fn($arg) { - echo $arg; -} -fn($a);//100 - -#引用传递 -function fn(&$arg){ - $arg = 10; -} -fn($a); -echo $a;//10; -# 默认值 -$a = 100; -function fn($arg = 10){ - echo $arg; -} -fn($a);//100 -fn();//10 -``` - -#### **类型声明** - -类型声明允许函数在调用时要求参数为特定类型。 如果给出的值类型不对,那么将会产生一个错误 - -目前支持的类型有类名、接口名、self、array、callable、bool、int、float、string - -```php -function fn(int $a){ - echo $a; -} -$c = "hello"; -fn($c);//error -``` - -#### **严格类型** - -默认情况下,如果能做到的话,PHP将会强迫错误类型的值转为函数期望的标量类型 - -但是在严格模式下,php不会转换。 - -```php -declare(strict_types=1); -function fn(int $a){ - echo $a; -} -$c = '1';//string -fn($c);// -``` - -#### **可变参数** - -PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 *...* 语法实现 - -```php -function fn(...$arg){ - foreach($arg as $v){ - echo $v; - } -} -fn(1,2,3,4); -``` - - - -### 返回值 - -函数的返回值可以通过return 返回。 - -```php -function fn(){ - return "hello"; -} -``` - -#### 返回值类型 - -可以限制返回值的类型 - -```php -declare(strict_types=1); -function($a):float { - return 1.1; -} -``` - -### 可变函数 - -PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数 - -```php -function f(){ - echo "1"; -} -$a = 'f'; - -$a();//1 -``` - -### 匿名函数 - -匿名函数(Anonymous functions),也叫闭包函数(*closures*),允许 临时创建一个没有指定名称的函数。最经常用作回调函数 参数的值。当然,也有其它应用的情况。 - -闭包可以从父作用域中继承变量。 任何此类变量都应该用 *use* 语言结构传递进去 - - - -```php -(function (){ - echo '匿名函数'; -})(); - -#传递参数 - -$a = function ($arg) { - echo $arg; -} -$arg = 'hello'; - -$a($arg);//hello; - -# 传递外部作用域变量 -$arg = 'arg'; -$f = function() use($arg){ - echo $arg; -} -$f(); -``` - +## 函数 + +函数是将特定的代码放到一个区间里,方便下次的调用。 + +函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。 + +PHP 中的所有函数和类都具有全局作用域。PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。 + +函数名是大小写无关的 + +要避免递归函数/方法调用超过 100-200 层,因为可能会使堆栈崩溃从而使当前脚本终止 + +```php +function 函数名字(参数){ + //函数体 +} +``` + +### 函数参数 + +可以通过参数传递外部的信息到函数内部。 + +支持引用传递参数、默认参数 + +```php +$a = 100; +function fn($arg) { + echo $arg; +} +fn($a);//100 + +#引用传递 +function fn(&$arg){ + $arg = 10; +} +fn($a); +echo $a;//10; +# 默认值 +$a = 100; +function fn($arg = 10){ + echo $arg; +} +fn($a);//100 +fn();//10 +``` + +#### **类型声明** + +类型声明允许函数在调用时要求参数为特定类型。 如果给出的值类型不对,那么将会产生一个错误 + +目前支持的类型有类名、接口名、self、array、callable、bool、int、float、string + +```php +function fn(int $a){ + echo $a; +} +$c = "hello"; +fn($c);//error +``` + +#### **严格类型** + +默认情况下,如果能做到的话,PHP将会强迫错误类型的值转为函数期望的标量类型 + +但是在严格模式下,php不会转换。 + +```php +declare(strict_types=1); +function fn(int $a){ + echo $a; +} +$c = '1';//string +fn($c);// +``` + +#### **可变参数** + +PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 *...* 语法实现 + +```php +function fn(...$arg){ + foreach($arg as $v){ + echo $v; + } +} +fn(1,2,3,4); +``` + + + +### 返回值 + +函数的返回值可以通过return 返回。 + +```php +function fn(){ + return "hello"; +} +``` + +#### 返回值类型 + +可以限制返回值的类型 + +```php +declare(strict_types=1); +function($a):float { + return 1.1; +} +``` + +### 可变函数 + +PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数 + +```php +function f(){ + echo "1"; +} +$a = 'f'; + +$a();//1 +``` + +### 匿名函数 + +匿名函数(Anonymous functions),也叫闭包函数(*closures*),允许 临时创建一个没有指定名称的函数。最经常用作回调函数 参数的值。当然,也有其它应用的情况。 + +闭包可以从父作用域中继承变量。 任何此类变量都应该用 *use* 语言结构传递进去 + + + +```php +(function (){ + echo '匿名函数'; +})(); + +#传递参数 + +$a = function ($arg) { + echo $arg; +} +$arg = 'hello'; + +$a($arg);//hello; + +# 传递外部作用域变量 +$arg = 'arg'; +$f = function() use($arg){ + echo $arg; +} +$f(); +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/6.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/6.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" index 065a01f..2f595e4 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/6.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/6.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" @@ -1,211 +1,211 @@ -## 面向对象 - -类:现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。比如人类、动物类 - -对象: 具有类类型的变量。比如人类中的张三这就是一个对象。他有人类的特性,比如说话写字等。 - -属性:类型的特征 比如人的名字、年龄、身高都是人的特征属性。 - -方法:方法是将一些操作封装起来的过程。类似函数。 - -继承:从父类那获得特征。比如人从父亲那继承了父亲的姓名。 - -### class (类) - -每个类的定义都以关键字 *class* 开头,后面跟着类名,后面跟着一对花括号,里面包含有类的属性与方法的定义 - -类名是非php保留字以外的合法的标签。合法的类名和变量一样。字母、下划线开头,后面跟任意字母或数字。 - -自 PHP 5.5 起,关键词 *class* 也可用于类名的解析。使用 *ClassName::class* 你可以获取一个字符串 - -```php -class Person { - public $name; - public $age; - - public function say(){ - echo "hello"; - } -} -echo Person::class;//Person 如果带命名空间。则会加上命名空间 - -``` - -要创建一个对象。使用new关键字 - -```php -$zhang = new Person(); -$zhang->name = '张三'; -``` - -### extends (继承) - -一个类可以在声明中用 *extends* 关键字继承另一个类的方法和属性。PHP不支持多重继承,一个类只能继承一个基类。 - -被继承的方法和属性可以通过用同样的名字重新声明被覆盖。如果父类的方法使用了final。则不会被覆盖。 - -当覆盖方法时,参数必须保持一致否则 PHP 将发出 **E_STRICT** 级别的错误信息。但构造函数例外,构造函数可在被覆盖时使用不同的参数 - -```php -class Boy extends Person { - -} -``` - -### 类常量 - -在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号 - -类常量是一个定值。类常量使用const 定义。访问的时候使用**self::**访问类常量 - -```php -class C{ - const PI = 3.1415926; - public function test(){ - echo self::PI; - } -} -``` - -### 自动加载 - -使用自动加载可以让我们不用一个个去include 类文件 - -自动加载不可用于 PHP 的 CLI - -```php -spl_autoload_register(function($class){ - require $class.".php"; -}) -``` - -### 构造函数、析构函数 - -构造函数在对象创建的时候,会被调用。适合初始化工作。 - -如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用**parent::__construct()** - -析构函数在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 - -试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。 - -```php -class P{ - public function __construct(){ - echo "construct"; - } - public function __destruct(){ - echo "destruct"; - } -} - -$p = new P();// construct; -unset($p);//destruct; - - -``` - -### 访问控制 - -对属性或方法的访问控制,是通过在前面添加关键字 *public*(公有),*protected*(受保护)或 *private*(私有)来实现的。 - -public 修饰的 可以在任何地方访问到 - -protected修饰的只能在子类或该类中访问 - -private修饰的只能在该类中访问。 - -```php -class A{ - public $name = 'a'; - protected $age = 10; - private $money = 100; -} -class B extends A{ - public function test(){ - echo $this->age;//a - - } - public function testPrivate(){ - echo $this->money; - } -} -$b = new B(); -echo $b->name;//a -echo $b->test();//10 -# 不可访问 -echo $b->age;//error; -#子类不能访问 -echo $b->testPrivate();//error -``` - -### 范围解析操作符(::) - -范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员,类常量。还可以用于覆盖类中的属性和方法。 - -self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的 - -当一个子类覆盖其父类中的方法时,PHP 不会调用父类中已被覆盖的方法。是否调用父类的方法取决于子类。使用self调用父类,使用$this 调用本类。 - -```php -class A{ - public $name = 'a'; - protected $age = 10; - private $money = 100; -} -class B extends A{ - public static $s = 's'; - const PI = 111; - public function test(){ - echo parent::age;// 10 - - } - - public static function testStatic(){ - echo self::$s; - } - public function testConst(){ - echo self::PI; - } - public function testPrivate(){ - echo $this->money; - } -} -# self 和 $this -class ParentClass { - function test() { - self::who(); // will output 'parent' - $this->who(); // will output 'child' - } - - function who() { - echo 'parent'; - } -} - -class ChildClass extends ParentClass { - function who() { - echo 'child'; - } -} - -$obj = new ChildClass(); -$obj->test();// -``` - -### static 静态关键字 - -声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以) - -静态属性不可以由对象通过 -> 操作符来访问。静态属性只能被初始化为文字或常量。静态属性不随着对象的销毁而销毁。 - -```php -class P{ - $a = "world"; - public static function test(){ - echo "hello".self::$a; - } -} -p::test(); -``` - +## 面向对象 + +类:现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。比如人类、动物类 + +对象: 具有类类型的变量。比如人类中的张三这就是一个对象。他有人类的特性,比如说话写字等。 + +属性:类型的特征 比如人的名字、年龄、身高都是人的特征属性。 + +方法:方法是将一些操作封装起来的过程。类似函数。 + +继承:从父类那获得特征。比如人从父亲那继承了父亲的姓名。 + +### class (类) + +每个类的定义都以关键字 *class* 开头,后面跟着类名,后面跟着一对花括号,里面包含有类的属性与方法的定义 + +类名是非php保留字以外的合法的标签。合法的类名和变量一样。字母、下划线开头,后面跟任意字母或数字。 + +自 PHP 5.5 起,关键词 *class* 也可用于类名的解析。使用 *ClassName::class* 你可以获取一个字符串 + +```php +class Person { + public $name; + public $age; + + public function say(){ + echo "hello"; + } +} +echo Person::class;//Person 如果带命名空间。则会加上命名空间 + +``` + +要创建一个对象。使用new关键字 + +```php +$zhang = new Person(); +$zhang->name = '张三'; +``` + +### extends (继承) + +一个类可以在声明中用 *extends* 关键字继承另一个类的方法和属性。PHP不支持多重继承,一个类只能继承一个基类。 + +被继承的方法和属性可以通过用同样的名字重新声明被覆盖。如果父类的方法使用了final。则不会被覆盖。 + +当覆盖方法时,参数必须保持一致否则 PHP 将发出 **E_STRICT** 级别的错误信息。但构造函数例外,构造函数可在被覆盖时使用不同的参数 + +```php +class Boy extends Person { + +} +``` + +### 类常量 + +在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号 + +类常量是一个定值。类常量使用const 定义。访问的时候使用**self::**访问类常量 + +```php +class C{ + const PI = 3.1415926; + public function test(){ + echo self::PI; + } +} +``` + +### 自动加载 + +使用自动加载可以让我们不用一个个去include 类文件 + +自动加载不可用于 PHP 的 CLI + +```php +spl_autoload_register(function($class){ + require $class.".php"; +}) +``` + +### 构造函数、析构函数 + +构造函数在对象创建的时候,会被调用。适合初始化工作。 + +如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用**parent::__construct()** + +析构函数在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 + +试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。 + +```php +class P{ + public function __construct(){ + echo "construct"; + } + public function __destruct(){ + echo "destruct"; + } +} + +$p = new P();// construct; +unset($p);//destruct; + + +``` + +### 访问控制 + +对属性或方法的访问控制,是通过在前面添加关键字 *public*(公有),*protected*(受保护)或 *private*(私有)来实现的。 + +public 修饰的 可以在任何地方访问到 + +protected修饰的只能在子类或该类中访问 + +private修饰的只能在该类中访问。 + +```php +class A{ + public $name = 'a'; + protected $age = 10; + private $money = 100; +} +class B extends A{ + public function test(){ + echo $this->age;//a + + } + public function testPrivate(){ + echo $this->money; + } +} +$b = new B(); +echo $b->name;//a +echo $b->test();//10 +# 不可访问 +echo $b->age;//error; +#子类不能访问 +echo $b->testPrivate();//error +``` + +### 范围解析操作符(::) + +范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员,类常量。还可以用于覆盖类中的属性和方法。 + +self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的 + +当一个子类覆盖其父类中的方法时,PHP 不会调用父类中已被覆盖的方法。是否调用父类的方法取决于子类。使用self调用父类,使用$this 调用本类。 + +```php +class A{ + public $name = 'a'; + protected $age = 10; + private $money = 100; +} +class B extends A{ + public static $s = 's'; + const PI = 111; + public function test(){ + echo $this->age;// 10 + + } + + public static function testStatic(){ + echo self::$s; + } + public function testConst(){ + echo self::PI; + } + public function testPrivate(){ + echo $this->money; + } +} +# self 和 $this +class ParentClass { + function test() { + self::who(); // will output 'parent' + $this->who(); // will output 'child' + } + + function who() { + echo 'parent'; + } +} + +class ChildClass extends ParentClass { + function who() { + echo 'child'; + } +} + +$obj = new ChildClass(); +$obj->test();// +``` + +### static 静态关键字 + +声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以) + +静态属性不可以由对象通过 -> 操作符来访问。静态属性只能被初始化为文字或常量。静态属性不随着对象的销毁而销毁。 + +```php +class P{ + $a = "world"; + public static function test(){ + echo "hello".self::$a; + } +} +p::test(); +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/7.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/7.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" index af32413..f4569fe 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/7.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/7.\351\235\242\345\220\221\345\257\271\350\261\241(OOP).md" @@ -1,336 +1,336 @@ -### 抽象类 - -从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。 - -php中如果一个类的方法被定义为抽象,那么该类就是抽象类。抽象类不能被实例化。只能被子类继承,子类必须实现全部的抽象方法,并且访问修饰必须和父类相同或者更宽松。参数必须一致。 - -```php -abstract class AbstractClass { - abstract public function test(); -} - -class Son extends AbstractClass{ - public function test(){ - echo "test"; - } -} -``` - -### 对象接口 - -接口泛指实体把自己提供给外界的一种[抽象化](可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。 - -使用接口不需要实现哪些方法,只需要定义这些方法。具体的实现由类去实现。 - -接口通过**interface**定义。实现接口通过**implements**。接口可以被继承 - -接口中也可以定义常量。但是不能定义属性。接口允许多继承 - -```php -interface Iter{ - public function test(); -} - -class ClassT implements Iter{ - public function test(){ - - } -} -interface Iter2 extends Iter { - public function test2(); -} - -class ClassT2 implements Iter2 { - public function test(){} - public function test2(){} -} - -#多继承 -interface Iter3 extends Iter1,Iter2{} - -/** -* An example of duck typing in PHP -*/ - -interface CanFly { - public function fly(); -} - -interface CanSwim { - public function swim(); -} - -class Bird { - public function info() { - echo "I am a {$this->name}\n"; - echo "I am an bird\n"; - } -} - -/** -* some implementations of birds -*/ -class Dove extends Bird implements CanFly { - var $name = "Dove"; - public function fly() { - echo "I fly\n"; - } -} - -class Penguin extends Bird implements CanSwim { - var $name = "Penguin"; - public function swim() { - echo "I swim\n"; - } -} - -class Duck extends Bird implements CanFly, CanSwim { - var $name = "Duck"; - public function fly() { - echo "I fly\n"; - } - public function swim() { - echo "I swim\n"; - } -} - -/** -* a simple function to describe a bird -*/ -function describe($bird) { - if ($bird instanceof Bird) { - $bird->info(); - if ($bird instanceof CanFly) { - $bird->fly(); - } - if ($bird instanceof CanSwim) { - $bird->swim(); - } - } else { - die("This is not a bird. I cannot describe it."); - } -} - -// describe these birds please -describe(new Penguin); -echo "---\n"; - -describe(new Dove); -echo "---\n"; - -describe(new Duck); -``` - -### Trait - -Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制.无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合 - -#### trait定义 - -```php -trait t{ - public function test(){} -} -# 使用 -class Test{ - use t; - public function test2{ - $this->test(); - } -} -``` - -#### 优先级 - -从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。 - -```php -class Base { - public function say(){ - echo "base"; - } -} -trait Test{ - public function say(){ - echo "trait"; - } -} - -class Son extends Base { - use Test; - public function test(){ - $this->say(); - } -} - -$s = new Son(); -$s->test();//trait -# 在子类中重写say。则调用子类的say方法 -public function say(){ - echo "son"; -} -``` - -#### 多个trait组合 - -通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中 - -```php -trait Hello { - public function sayHello() { - echo 'Hello '; - } -} - -trait World { - public function sayWorld() { - echo 'World'; - } -} - -class MyHelloWorld { - use Hello, World; - public function sayExclamationMark() { - echo '!'; - } -} -``` - -#### 冲突的解决 - -如果多个trait中。都有同名的方法,则会产生冲突,冲突会产生一个致命的错误。 - -为了解决多个 trait 在同一个类中的命名冲突,需要使用 *insteadof* 操作符来明确指定使用冲突方法中的哪一个 - -*as* 操作符可以 为某个方法引入别名 - -```php -trait A { - public function smallTalk() { - echo 'a'; - } - public function bigTalk() { - echo 'A'; - } -} - -trait B { - public function smallTalk() { - echo 'b'; - } - public function bigTalk() { - echo 'B'; - } -} - -class Talker { - use A, B { - B::smallTalk insteadof A; - A::bigTalk insteadof B; - } -} - -class Aliased_Talker { - use A, B { - B::smallTalk insteadof A; - A::bigTalk insteadof B; - B::bigTalk as talk; - } -} -``` - -#### 使用as修改访问控制 - -```php -class Base { - public function say(){ - echo "base"; - } -} -trait Test{ - public function say(){ - echo "trait"; - } -} - -class Son extends Base { - use Test {say as private say2;} - public function say(){ - echo "son"; - } -} - -$s = new Son(); -$s->say2();//error -``` - - - -#### 使用多个trait组合 - -```php -trait A{} -trait B{} -trait C{use A,B;} -``` - -#### trait抽象成员方法 - -为了对使用的类施加强制要求,trait 支持抽象方法的使用 - -```php -trait T{ - abstract public function test(); -} - -class Test{ - use T; - public function test(){} -} -``` - -#### trait 静态方法 - -```php -trait T{ - public static function test() {}; -} - -class Test{ - use T; -} -Test::test(); -``` - -#### trait定义属性 - -Trait 定义了一个属性后,类就不能定义同样名称的属性 - -```php -trait PropertiesTrait { - public $same = true; - public $different = false; -} - -class PropertiesExample { - use PropertiesTrait; - public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒 - public $different = true; // 致命错误 - -``` - -### 匿名类 - -**PHP 7** 开始支持匿名类。 匿名类很有用,可以创建一次性的简单对象 - -匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。 为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。 为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来. - -```php -$a = new class { - public function test(){ - echo "test"; - } -}; -$a->test(); -``` - +### 抽象类 + +从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。 + +php中如果一个类的方法被定义为抽象,那么该类就是抽象类。抽象类不能被实例化。只能被子类继承,子类必须实现全部的抽象方法,并且访问修饰必须和父类相同或者更宽松。参数必须一致。 + +```php +abstract class AbstractClass { + abstract public function test(); +} + +class Son extends AbstractClass{ + public function test(){ + echo "test"; + } +} +``` + +### 对象接口 + +接口泛指实体把自己提供给外界的一种[抽象化](可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。 + +使用接口不需要实现哪些方法,只需要定义这些方法。具体的实现由类去实现。 + +接口通过**interface**定义。实现接口通过**implements**。接口可以被继承 + +接口中也可以定义常量。但是不能定义属性。接口允许多继承 + +```php +interface Iter{ + public function test(); +} + +class ClassT implements Iter{ + public function test(){ + + } +} +interface Iter2 extends Iter { + public function test2(); +} + +class ClassT2 implements Iter2 { + public function test(){} + public function test2(){} +} + +#多继承 +interface Iter3 extends Iter1,Iter2{} + +/** +* An example of duck typing in PHP +*/ + +interface CanFly { + public function fly(); +} + +interface CanSwim { + public function swim(); +} + +class Bird { + public function info() { + echo "I am a {$this->name}\n"; + echo "I am an bird\n"; + } +} + +/** +* some implementations of birds +*/ +class Dove extends Bird implements CanFly { + var $name = "Dove"; + public function fly() { + echo "I fly\n"; + } +} + +class Penguin extends Bird implements CanSwim { + var $name = "Penguin"; + public function swim() { + echo "I swim\n"; + } +} + +class Duck extends Bird implements CanFly, CanSwim { + var $name = "Duck"; + public function fly() { + echo "I fly\n"; + } + public function swim() { + echo "I swim\n"; + } +} + +/** +* a simple function to describe a bird +*/ +function describe($bird) { + if ($bird instanceof Bird) { + $bird->info(); + if ($bird instanceof CanFly) { + $bird->fly(); + } + if ($bird instanceof CanSwim) { + $bird->swim(); + } + } else { + die("This is not a bird. I cannot describe it."); + } +} + +// describe these birds please +describe(new Penguin); +echo "---\n"; + +describe(new Dove); +echo "---\n"; + +describe(new Duck); +``` + +### Trait + +Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制.无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合 + +#### trait定义 + +```php +trait t{ + public function test(){} +} +# 使用 +class Test{ + use t; + public function test2{ + $this->test(); + } +} +``` + +#### 优先级 + +从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。 + +```php +class Base { + public function say(){ + echo "base"; + } +} +trait Test{ + public function say(){ + echo "trait"; + } +} + +class Son extends Base { + use Test; + public function test(){ + $this->say(); + } +} + +$s = new Son(); +$s->test();//trait +# 在子类中重写say。则调用子类的say方法 +public function say(){ + echo "son"; +} +``` + +#### 多个trait组合 + +通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中 + +```php +trait Hello { + public function sayHello() { + echo 'Hello '; + } +} + +trait World { + public function sayWorld() { + echo 'World'; + } +} + +class MyHelloWorld { + use Hello, World; + public function sayExclamationMark() { + echo '!'; + } +} +``` + +#### 冲突的解决 + +如果多个trait中。都有同名的方法,则会产生冲突,冲突会产生一个致命的错误。 + +为了解决多个 trait 在同一个类中的命名冲突,需要使用 *insteadof* 操作符来明确指定使用冲突方法中的哪一个 + +*as* 操作符可以 为某个方法引入别名 + +```php +trait A { + public function smallTalk() { + echo 'a'; + } + public function bigTalk() { + echo 'A'; + } +} + +trait B { + public function smallTalk() { + echo 'b'; + } + public function bigTalk() { + echo 'B'; + } +} + +class Talker { + use A, B { + B::smallTalk insteadof A; + A::bigTalk insteadof B; + } +} + +class Aliased_Talker { + use A, B { + B::smallTalk insteadof A; + A::bigTalk insteadof B; + B::bigTalk as talk; + } +} +``` + +#### 使用as修改访问控制 + +```php +class Base { + public function say(){ + echo "base"; + } +} +trait Test{ + public function say(){ + echo "trait"; + } +} + +class Son extends Base { + use Test {say as private say2;} + public function say(){ + echo "son"; + } +} + +$s = new Son(); +$s->say2();//error +``` + + + +#### 使用多个trait组合 + +```php +trait A{} +trait B{} +trait C{use A,B;} +``` + +#### trait抽象成员方法 + +为了对使用的类施加强制要求,trait 支持抽象方法的使用 + +```php +trait T{ + abstract public function test(); +} + +class Test{ + use T; + public function test(){} +} +``` + +#### trait 静态方法 + +```php +trait T{ + public static function test() {}; +} + +class Test{ + use T; +} +Test::test(); +``` + +#### trait定义属性 + +Trait 定义了一个属性后,类就不能定义同样名称的属性 + +```php +trait PropertiesTrait { + public $same = true; + public $different = false; +} + +class PropertiesExample { + use PropertiesTrait; + public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒 + public $different = true; // 致命错误 + +``` + +### 匿名类 + +**PHP 7** 开始支持匿名类。 匿名类很有用,可以创建一次性的简单对象 + +匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。 为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。 为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来. + +```php +$a = new class { + public function test(){ + echo "test"; + } +}; +$a->test(); +``` + diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/8.\351\235\242\345\220\221\345\257\271\350\261\241\357\274\210OOP\357\274\211.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/8.\351\235\242\345\220\221\345\257\271\350\261\241\357\274\210OOP\357\274\211.md" index d7726a1..4d174e9 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/8.\351\235\242\345\220\221\345\257\271\350\261\241\357\274\210OOP\357\274\211.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/8.\351\235\242\345\220\221\345\257\271\350\261\241\357\274\210OOP\357\274\211.md" @@ -1,236 +1,236 @@ -### 重写、重载 - -override是重写(覆盖)了一个方法,以实现不同的功能。一般是用于子类在继承父类时,重写(重新实现)父类中的方法。 - -**重写(覆盖)的规则**: -   1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载. -   2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。 -   3、重写的方法的返回值必须和被重写的方法的返回一致; -   4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类; -   5、被重写的方法不能为**private**,否则在其子类中只是新定义了一个方法,并没有对其进行重写 - - 6、静态方法不能被重写为非静态的方法(会编译出错)。 - -overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。 - -**重载的规则**: -   1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样); -   2、不能通过访问权限、返回类型、抛出的异常进行重载; -   3、方法的异常类型和数目不会对重载造成影响; - -PHP所提供的"重载"(overloading)是指动态地"创建"类属性和方法。我们是通过魔术方法(magic methods)来实现的。 - -```php -class A{ - public function test(){ - echo "11"; - } -} - -class B extends A{ - #重写父类的方法 - public function test(){ - echo "22"; - } -} - -class C{ - public function __call($name,$args) { - echo $name; - } - public function __callStatic($name, $arguments){} -} -``` - -### 多态 - -多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。 - -```php -abstract class animal{ - abstract function fun(); -} -class cat extends animal{ - function fun(){ - echo "cat say miaomiao..."; - } -} -class dog extends animal{ - function fun(){ - echo "dog say wangwang..."; - } -} -function work($obj){ - if($obj instanceof animal){ - $obj -> fun(); - }else{ - echo "no function"; - } -} -work(new dog()); -work(new cat()); -``` - -### 遍历对象 - -遍历对象可以使用foreach遍历可见属性。或者实现iterator接口 - -```php -class MyClass -{ - public $var1 = 'value 1'; - public $var2 = 'value 2'; - public $var3 = 'value 3'; - - protected $protected = 'protected var'; - private $private = 'private var'; - -} -$c = new MyClass(); - -foreach ($c as $key=>$v) { - echo $key."=>"$v; -} - -``` - -### 魔术方法 - -- __construct 初始化调用 -- __desturct 对象销毁时调用 -- __call 访问一个不存在的方法的时候调用 -- __callStatic 访问一个不存在的静态方法调用 -- __get() 访问一个不存在的属性调用 -- __set() 修改一个不存在的属性调用 -- __isset() 使用isset判断一个高属性的时候调用 -- __toString() 当一个对象以一个字符串返回时候触发调用 -- __invoke()当把一个对象当初函数去调用的时候 触发 - - - -### final - -final 最终的,如果一个类被定位成final 这个类不能被继承。如果一个方法被定义一个final。这个方法不能被覆盖。 - -final不能修饰属性。 - -```php -class A{ - final public function test(){} -} - -Class B extends A{ - public function test(){ //error - - } -} -``` - -### 对象复制、对象比较 - -对象复制可以通过 **clone** 关键字来完成 - -当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。 - -```php -class A{ - - public $name = "hello"; -} - -$a = new A(); - -$b = clone $a; -``` - -当使用比较运算符(*==*)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。 - -而如果使用全等运算符(*===*),这两个对象变量一定要指向某个类的同一个实例(即同一个对象) - -### 类型约束 - -函数的参数可以指定必须为对象(在函数原型里面指定类的名字),接口,数组 - -```php -function Test(A $a){} -``` - -### 后期静态绑定 - -“后期绑定”的意思是说,*static::* 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用 - -**self的限制** - -使用 *self::* 或者 *__CLASS__* 对当前类的静态引用,取决于定义当前方法所在的类 - -```php - -class A { - public static function who() { - echo __CLASS__; - } - public static function test() { - self::who(); - } -} - -class B extends A { - public static function who() { - echo __CLASS__; - } -}// - -B::test();//A -#静态绑定语法 -class A { - public static function who() { - echo __CLASS__; - } - public static function test() { - static::who(); - } -} - -class B extends A { - public static function who() { - echo __CLASS__; - } -}// - -B::test();//B -## 实现ar model -class Model -{ - public static function find() - { - echo static::$name; - } -} - -class Product extends Model -{ - protected static $name = 'Product'; -} - -Product::find(); -``` - -### 对象和引用 - -> PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。在 PHP 5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。 当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。 - -```php -class A { - public $foo = 1; -} - -$a = new A; -$b = $a; // $a ,$b都是同一个标识符的拷贝 - // ($a) = ($b) = -$b->foo = 2; -echo $a->foo."\n"; -``` - -### 对象序列化 - +### 重写、重载 + +override是重写(覆盖)了一个方法,以实现不同的功能。一般是用于子类在继承父类时,重写(重新实现)父类中的方法。 + +**重写(覆盖)的规则**: +   1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载. +   2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。 +   3、重写的方法的返回值必须和被重写的方法的返回一致; +   4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类; +   5、被重写的方法不能为**private**,否则在其子类中只是新定义了一个方法,并没有对其进行重写 + + 6、静态方法不能被重写为非静态的方法(会编译出错)。 + +overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。 + +**重载的规则**: +   1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样); +   2、不能通过访问权限、返回类型、抛出的异常进行重载; +   3、方法的异常类型和数目不会对重载造成影响; + +PHP所提供的"重载"(overloading)是指动态地"创建"类属性和方法。我们是通过魔术方法(magic methods)来实现的。 + +```php +class A{ + public function test(){ + echo "11"; + } +} + +class B extends A{ + #重写父类的方法 + public function test(){ + echo "22"; + } +} + +class C{ + public function __call($name,$args) { + echo $name; + } + public function __callStatic($name, $arguments){} +} +``` + +### 多态 + +多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。 + +```php +abstract class animal{ + abstract function fun(); +} +class cat extends animal{ + function fun(){ + echo "cat say miaomiao..."; + } +} +class dog extends animal{ + function fun(){ + echo "dog say wangwang..."; + } +} +function work($obj){ + if($obj instanceof animal){ + $obj -> fun(); + }else{ + echo "no function"; + } +} +work(new dog()); +work(new cat()); +``` + +### 遍历对象 + +遍历对象可以使用foreach遍历可见属性。或者实现iterator接口 + +```php +class MyClass +{ + public $var1 = 'value 1'; + public $var2 = 'value 2'; + public $var3 = 'value 3'; + + protected $protected = 'protected var'; + private $private = 'private var'; + +} +$c = new MyClass(); + +foreach ($c as $key=>$v) { + echo $key."=>"$v; +} + +``` + +### 魔术方法 + +- __construct 初始化调用 +- __desturct 对象销毁时调用 +- __call 访问一个不存在的方法的时候调用 +- __callStatic 访问一个不存在的静态方法调用 +- __get() 访问一个不存在的属性调用 +- __set() 修改一个不存在的属性调用 +- __isset() 使用isset判断一个高属性的时候调用 +- __toString() 当一个对象以一个字符串返回时候触发调用 +- __invoke()当把一个对象当初函数去调用的时候 触发 + + + +### final + +final 最终的,如果一个类被定位成final 这个类不能被继承。如果一个方法被定义一个final。这个方法不能被覆盖。 + +final不能修饰属性。 + +```php +class A{ + final public function test(){} +} + +Class B extends A{ + public function test(){ //error + + } +} +``` + +### 对象复制、对象比较 + +对象复制可以通过 **clone** 关键字来完成 + +当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。 + +```php +class A{ + + public $name = "hello"; +} + +$a = new A(); + +$b = clone $a; +``` + +当使用比较运算符(*==*)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。 + +而如果使用全等运算符(*===*),这两个对象变量一定要指向某个类的同一个实例(即同一个对象) + +### 类型约束 + +函数的参数可以指定必须为对象(在函数原型里面指定类的名字),接口,数组 + +```php +function Test(A $a){} +``` + +### 后期静态绑定 + +“后期绑定”的意思是说,*static::* 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用 + +**self的限制** + +使用 *self::* 或者 *__CLASS__* 对当前类的静态引用,取决于定义当前方法所在的类 + +```php + +class A { + public static function who() { + echo __CLASS__; + } + public static function test() { + self::who(); + } +} + +class B extends A { + public static function who() { + echo __CLASS__; + } +}// + +B::test();//A +#静态绑定语法 +class A { + public static function who() { + echo __CLASS__; + } + public static function test() { + static::who(); + } +} + +class B extends A { + public static function who() { + echo __CLASS__; + } +}// + +B::test();//B +## 实现ar model +class Model +{ + public static function find() + { + echo static::$name; + } +} + +class Product extends Model +{ + protected static $name = 'Product'; +} + +Product::find(); +``` + +### 对象和引用 + +> PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。在 PHP 5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。 当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。 + +```php +class A { + public $foo = 1; +} + +$a = new A; +$b = $a; // $a ,$b都是同一个标识符的拷贝 + // ($a) = ($b) = +$b->foo = 2; +echo $a->foo."\n"; +``` + +### 对象序列化 + 在会话中存储对象。使用serialize序列化。 \ No newline at end of file diff --git "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/9.\345\221\275\345\220\215\347\251\272\351\227\264.md" "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/9.\345\221\275\345\220\215\347\251\272\351\227\264.md" index 14ccdc8..5f1645d 100644 --- "a/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/9.\345\221\275\345\220\215\347\251\272\351\227\264.md" +++ "b/PHP/PHP\346\211\213\345\206\214\347\254\224\350\256\260/9.\345\221\275\345\220\215\347\251\272\351\227\264.md" @@ -1,148 +1,148 @@ -## 命名空间 - -命名空间简单的说就是类似文件的路径。为了解决命名冲突的问题产生的。比如在我们的代码中经常遇到以下问题。 - -自己在一个目录写了一个类叫A. 另外一个人也写了一个类叫A。就会产生一个冲突。最初的框架,比如ci框架,都是类的名字前面加上一CI_的前缀防止命名冲突。 - -命令空间的思想来源于路径。比如在一个/www/目录下不能存在两个相同的文件。但是可以文件放在/www/a/ 、/www/b下面,这样两个文件就不会冲突。同理命名空间。如果在\www\a\A、\www\b\A 这两个类也可以可以同时存在的。 - -命名空间的定义使用**namespace** 定义。只有类(包括抽象类和traits)、接口、函数和常量受到命名空间的影响。 - -命名空间的定义必须在脚本的首行。不能有bom,否则会出错。 - -定义子命名空间用\分割。 - -也可以在一个文件定义多个命名空间。但是一般不这么做。 - -```php -namespace app; -#子命名空间 -namespace app\model; - - -namespace app { - function test(){} -} -namespace app2{ - function test(){} -} -namespace { - app\test(); - app2\test(); -} - -``` - -### 命名空间基础 - -命名空间和路径原理相似。所以有相对、绝对 - -- 非限定名称、不包含前缀的类名称。类似a.php 比如include('a.php') - - ```php - $a = new Test();// 如果这个文件定义的命名空间是app。那么就是访问的 app\Test类。 - ``` - -- 限定名称,或包含前缀 类似 文件路径中的 a/c.php这种形式 - - ```php - $a = new model\Test(); //如果该文件的命名空间是app。则访问的就是app\model\Test - ``` - -- 完全限制 类似文件中的绝对路径 /www/a.php - - ```php - $a = new \app\Test(); - ``` - -### 别名和导入 - -命名空间支持别名和引入外部的名字 - -所有支持命名空间的PHP版本支持三种别名或导入方式:为类名称使用别名、为接口使用别名或为命名空间名称使用别名 - - - -```php -namespace a; -use app\model; -use app\test\model as model2; - -``` - -### 全局命名空间 - -如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 *\\* 表示该名称是全局空间中的名称 - -```php -namespace app; - -function test(){ - new \Redis(); -} -``` - - - -### 名词解析 - -```php -namespace A; -use B\D, C\E as F; - -// 函数调用 - -foo(); // 首先尝试调用定义在命名空间"A"中的函数foo() - // 再尝试调用全局函数 "foo" - -\foo(); // 调用全局空间函数 "foo" - -my\foo(); // 调用定义在命名空间"A\my"中函数 "foo" - -F(); // 首先尝试调用定义在命名空间"A"中的函数 "F" - // 再尝试调用全局函数 "F" - -// 类引用 - -new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象 - // 如果未找到,则尝试自动装载类 "A\B" - -new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象 - // 如果未找到,则尝试自动装载类 "B\D" - -new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象 - // 如果未找到,则尝试自动装载类 "C\E" - -new \B(); // 创建定义在全局空间中的类 "B" 的一个对象 - // 如果未发现,则尝试自动装载类 "B" - -new \D(); // 创建定义在全局空间中的类 "D" 的一个对象 - // 如果未发现,则尝试自动装载类 "D" - -new \F(); // 创建定义在全局空间中的类 "F" 的一个对象 - // 如果未发现,则尝试自动装载类 "F" - -// 调用另一个命名空间中的静态方法或命名空间函数 - -B\foo(); // 调用命名空间 "A\B" 中函数 "foo" - -B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 - // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B" - -D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法 - // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D" - -\B\foo(); // 调用命名空间 "B" 中的函数 "foo" - -\B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法 - // 如果类 "B" 未找到,则尝试自动装载类 "B" - -// 当前命名空间中的静态方法或函数 - -A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法 - // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B" - -\A\B::foo(); // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法 - // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B" -``` - +## 命名空间 + +命名空间简单的说就是类似文件的路径。为了解决命名冲突的问题产生的。比如在我们的代码中经常遇到以下问题。 + +自己在一个目录写了一个类叫A. 另外一个人也写了一个类叫A。就会产生一个冲突。最初的框架,比如ci框架,都是类的名字前面加上一CI_的前缀防止命名冲突。 + +命令空间的思想来源于路径。比如在一个/www/目录下不能存在两个相同的文件。但是可以文件放在/www/a/ 、/www/b下面,这样两个文件就不会冲突。同理命名空间。如果在\www\a\A、\www\b\A 这两个类也可以可以同时存在的。 + +命名空间的定义使用**namespace** 定义。只有类(包括抽象类和traits)、接口、函数和常量受到命名空间的影响。 + +命名空间的定义必须在脚本的首行。不能有bom,否则会出错。 + +定义子命名空间用\分割。 + +也可以在一个文件定义多个命名空间。但是一般不这么做。 + +```php +namespace app; +#子命名空间 +namespace app\model; + + +namespace app { + function test(){} +} +namespace app2{ + function test(){} +} +namespace { + app\test(); + app2\test(); +} + +``` + +### 命名空间基础 + +命名空间和路径原理相似。所以有相对、绝对 + +- 非限定名称、不包含前缀的类名称。类似a.php 比如include('a.php') + + ```php + $a = new Test();// 如果这个文件定义的命名空间是app。那么就是访问的 app\Test类。 + ``` + +- 限定名称,或包含前缀 类似 文件路径中的 a/c.php这种形式 + + ```php + $a = new model\Test(); //如果该文件的命名空间是app。则访问的就是app\model\Test + ``` + +- 完全限制 类似文件中的绝对路径 /www/a.php + + ```php + $a = new \app\Test(); + ``` + +### 别名和导入 + +命名空间支持别名和引入外部的名字 + +所有支持命名空间的PHP版本支持三种别名或导入方式:为类名称使用别名、为接口使用别名或为命名空间名称使用别名 + + + +```php +namespace a; +use app\model; +use app\test\model as model2; + +``` + +### 全局命名空间 + +如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 *\\* 表示该名称是全局空间中的名称 + +```php +namespace app; + +function test(){ + new \Redis(); +} +``` + + + +### 名词解析 + +```php +namespace A; +use B\D, C\E as F; + +// 函数调用 + +foo(); // 首先尝试调用定义在命名空间"A"中的函数foo() + // 再尝试调用全局函数 "foo" + +\foo(); // 调用全局空间函数 "foo" + +my\foo(); // 调用定义在命名空间"A\my"中函数 "foo" + +F(); // 首先尝试调用定义在命名空间"A"中的函数 "F" + // 再尝试调用全局函数 "F" + +// 类引用 + +new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象 + // 如果未找到,则尝试自动装载类 "A\B" + +new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象 + // 如果未找到,则尝试自动装载类 "B\D" + +new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象 + // 如果未找到,则尝试自动装载类 "C\E" + +new \B(); // 创建定义在全局空间中的类 "B" 的一个对象 + // 如果未发现,则尝试自动装载类 "B" + +new \D(); // 创建定义在全局空间中的类 "D" 的一个对象 + // 如果未发现,则尝试自动装载类 "D" + +new \F(); // 创建定义在全局空间中的类 "F" 的一个对象 + // 如果未发现,则尝试自动装载类 "F" + +// 调用另一个命名空间中的静态方法或命名空间函数 + +B\foo(); // 调用命名空间 "A\B" 中函数 "foo" + +B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 + // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B" + +D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法 + // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D" + +\B\foo(); // 调用命名空间 "B" 中的函数 "foo" + +\B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法 + // 如果类 "B" 未找到,则尝试自动装载类 "B" + +// 当前命名空间中的静态方法或函数 + +A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法 + // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B" + +\A\B::foo(); // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法 + // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B" +``` + diff --git "a/PHP/PHP\350\277\220\350\241\214\345\216\237\347\220\206.md" "b/PHP/PHP\350\277\220\350\241\214\345\216\237\347\220\206.md" index f895fd0..c1515fb 100644 --- "a/PHP/PHP\350\277\220\350\241\214\345\216\237\347\220\206.md" +++ "b/PHP/PHP\350\277\220\350\241\214\345\216\237\347\220\206.md" @@ -4,13 +4,16 @@ PHP是一种适用于web开发的动态语言。具体点说,就是一个用C 了解PHP底层实现的目的是什么?动态语言要像用好首先得了解它,内存管理、框架模型值得我们借鉴,通过扩展开发实现更多更强大的功能,优化我们程序的性能。 -1. PHP的设计理念及特点 +### **PHP的设计理念及特点** + 多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响。当然,随着时代发展,PHP也早已支持多线程模型。 弱类型语言:和C/C++、Java、C#等语言不同,PHP是一门弱类型语言。一个变量的类型并不是一开始就确定不变,运行中才会确定并可能发生隐式或显式的类型转换,这种机制的灵活性在web开发中非常方便、高效,具体会在后面PHP变量中详述。 引擎(Zend)+组件(ext)的模式降低内部耦合。 中间层(sapi)隔绝web server和PHP。 语法简单灵活,没有太多规范。缺点导致风格混杂,但再差的程序员也不会写出太离谱危害全局的程序。 -2. PHP的四层体系 + +### PHP的四层体系 + PHP的核心架构如下图: php-core @@ -21,22 +24,40 @@ php-core 从图上可以看出,PHP从下到上是一个4层体系: -Zend引擎:Zend整体用纯C实现,是PHP的内核部分,它将PHP代码翻译(词法、语法解析等一系列编译过程)为可执行opcode处理,并实现相应的处理方法,实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕Zend实现。 -Extensions:围绕着Zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的PHP中间层、富文本解析就是extension的典型应用)。 -Sapi:Sapi全称是Server Application Programming Interface,也就是服务端应用编程接口,Sapi通过一系列钩子函数,使得PHP可以和外围交互数据,这是PHP非常优雅和成功的一个设计,通过sapi成功的将PHP本身和上层应用解耦隔离,PHP可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。 -Application:这就是我们平时编写的PHP程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。 +1. Zend引擎: + + Zend整体用纯C实现,是PHP的内核部分,它将PHP代码翻译(词法、语法解析等一系列编译过程)为可执行opcode处理,并实现相应的处理方法,实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕Zend实现。 + +2. Extensions: + + 围绕着Zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的PHP中间层、富文本解析就是extension的典型应用)。 + +3. Sapi: + + Sapi全称是Server Application Programming Interface,也就是服务端应用编程接口,Sapi通过一系列钩子函数,使得PHP可以和外围交互数据,这是PHP非常优雅和成功的一个设计,通过sapi成功的将PHP本身和上层应用解耦隔离,PHP可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。 + +4. Application: + + 这就是我们平时编写的PHP程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。 + 如果PHP是一辆车,那么车的框架就是PHP本身,Zend是车的引擎(发动机),Ext下面的各种组件就是车的轮子,Sapi可以看做是公路,车可以跑在不同类型的公路上,而一次PHP程序的执行就是汽车跑在公路上。因此,我们需要:性能优异的引擎+合适的车轮+正确的跑道。 -3. Sapi -如前所述,Sapi通过通过一系列的接口,使得外部应用可以和PHP交换数据,并可以根据不同应用特点实现特定的处理方法,我们常见的一些sapi有: +### SAPI + +如前所述,SAPI通过通过一系列的接口,使得外部应用可以和PHP交换数据,并可以根据不同应用特点实现特定的处理方法,我们常见的一些sapi有: + +**apache2handler**:这是以apache作为webserver,采用mod_PHP模式运行时候的处理方式。 +**cgi**:这是webserver和PHP直接的另一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP得到越来越多的应用,也是异步webserver所唯一支持的方式。 +**cli**:命令行调用的应用模式 + -apache2handler:这是以apache作为webserver,采用mod_PHP模式运行时候的处理方式。 -cgi:这是webserver和PHP直接的另一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP得到越来越多的应用,也是异步webserver所唯一支持的方式。 -cli:命令行调用的应用模式 -4. PHP的执行流程&opcode - 我们先来看看PHP代码的执行所经过的流程。 - ![img](https://www.awaimai.com/wp-content/uploads/2016/02/2011_09_20_02.jpg) +### PHP的执行流程&opcode + + +我们先来看看PHP代码的执行所经过的流程。 + +![img](https://www.awaimai.com/wp-content/uploads/2016/02/2011_09_20_02.jpg) 从图上可以看到,PHP实现了一个典型的动态语言执行过程:拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcodes),然后ZEND虚拟机顺次执行这些指令完成操作。PHP本身是用C实现的,因此最终调用的也都是C的函数,实际上,我们可以把PHP看做是一个C开发的软件。 @@ -59,7 +80,10 @@ ZEND_IS_EQUAL_SPEC_CV_CONST:判断相等 $a==1 ZEND_IS_IDENTICAL_SPEC_CV_CONST:判断相等 $a===1 -5. HashTable — 核心数据结构 + + +### **HashTable — 核心数据结构** + HashTable是Zend的核心数据结构,在PHP里面几乎并用来实现所有常见功能,我们知道的PHP数组即是其典型应用,此外,在zend内部,如函数符号表、全局变量等也都是基于hash table来实现。 PHP的hash table具有如下特点: @@ -78,18 +102,22 @@ Zend hash table实现了典型的hash表散列结构,同时通过附加一个 散列结构:Zend的散列结构是典型的hash表模型,通过链表的方式来解决冲突。需要注意的是zend的hash table是一个自增长的数据结构,当hash表数目满了之后,其本身会动态以2倍的方式扩容并重新元素位置。初始大小均为8。另外,在进行key->value快速查找时候,zend本身还做了一些优化,通过空间换时间的方式加快速度。比如在每个元素中都会用一个变量nKeyLength标识key的长度以作快速判定。 双向链表:Zend hash table通过一个链表结构,实现了元素的线性遍历。理论上,做遍历使用单向链表就够了,之所以使用双向链表,主要目的是为了快速删除,避免遍历。Zend hash table是一种复合型的结构,作为数组使用时,即支持常见的关联数组也能够作为顺序索引数字来使用,甚至允许2者的混合。 PHP关联数组:关联数组是典型的hash_table应用。一次查询过程经过如下几步(从代码可以看出,这是一个常见的hash查询过程,并增加一些快速判定加速查找。): +```c getKeyHashValue h; index = n & nTableMask; Bucket *p = arBucket[index]; while (p) { if ((p->h == h) & (p->nKeyLength == nKeyLength)) { - RETURN p->data; + return p->data; } p=p->next; } -RETURN FALTURE; +return FALTURE; +``` PHP索引数组:索引数组就是我们常见的数组,通过下标访问。例如 $arr[0],Zend HashTable内部进行了归一化处理,对于index类型key同样分配了hash值和nKeyLength(为0)。内部成员变量nNextFreeElement就是当前分配到的最大id,每次push后自动加一。正是这种归一化处理,PHP才能够实现关联和非关联的混合。由于push操作的特殊性,索引key在PHP数组中先后顺序并不是通过下标大小来决定,而是由push的先后决定。例如 $arr[1] = 2; $arr[2] = 3; 对于double类型的key,Zend HashTable会将他当做索引key处理 -6. PHP变量 + +### PHP变量 + PHP是一门弱类型语言,本身不严格区分变量的类型。PHP在变量申明的时候不需要指定类型。PHP在程序运行期间可能进行变量类型的隐示转换。和其他强类型语言一样,程序中也可以进行显示的类型转换。PHP变量可以分为简单类型(int、string、bool)、集合类型(array resource object)和常量(const)。以上所有的变量在底层都是同一种结构 zval。 Zval是zend中另一个非常重要的数据结构,用来标识并实现PHP变量,其数据结构如下: @@ -118,7 +146,8 @@ PHP变量通过引用计数实现变量共享数据,那如果改变其中一 对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。 -整数和浮点数 +### 整数和浮点数 + 整数、浮点数是PHP中的基础类型之一,也是一个简单型变量。对于整数和浮点数,在zvalue中直接存储对应的值。其类型分别是long和double。 从zvalue结构中可以看出,对于整数类型,和c等强类型语言不同,PHP是不区分int、unsigned int、long、long long等类型的,对它来说,整数只有一种类型也就是long。由此,可以看出,在PHP里面,整数的取值范围是由编译器位数来决定而不是固定不变的。 @@ -127,7 +156,8 @@ PHP变量通过引用计数实现变量共享数据,那如果改变其中一 在PHP中,如果整数范围越界了怎么办?这种情况下会自动转换为double类型,这个一定要小心,很多trick都是由此产生。 -字符和字符串 +### **字符和字符串** + 和整数一样,字符变量也是PHP中的基础类型和简单型变量。通过zvalue结构可以看出,在PHP中,字符串是由由指向实际数据的指针和长度结构体组成,这点和c++中的string比较类似。由于通过一个实际变量表示长度,和c不同,它的字符串可以是2进制数据(包含\0),同时在PHP中,求字符串长度strlen是O(1)操作。 在新增、修改、追加字符串操作时,PHP都会重新分配内存生成新的字符串。最后,出于安全考虑,PHP在生成一个字符串时末尾仍然会添加\0。 @@ -136,20 +166,21 @@ PHP变量通过引用计数实现变量共享数据,那如果改变其中一 假设有如下4个变量: -```php $strA = '123'; - -``` - +```php +$strA = '123'; $strB = '456'; $intA = 123; $intB = 456; +``` 现在对如下的几种字符串拼接方式做一个比较和说明: // 下面两张情况,zend会重新malloc一块内存并进行相应处理,其速度一般 +```php $res = $strA . $strB $res = "$strA$strB" - +``` // 这种是速度最快的,zend会在当前strA基础上直接relloc,避免重复拷贝 +```php $strA = $strA . $strB // 这种速度较慢,因为需要做隐式的格式转换,实际编写程序中也应该注意尽量避免 @@ -159,12 +190,16 @@ $res = $intA . $intB // 本身对于格式识别和处理就需要耗费比较多时间,另外本身机制也是malloc。 // 不过sprintf的方式最具可读性,实际中可以根据具体情况灵活选择。 $strA = sprintf ("%s%s", $strA . $strB); -数组 +``` + +### **数组** + PHP的数组通过Zend HashTable来天然实现。 foreach操作如何实现?对一个数组的foreach就是通过遍历hashtable中的双向链表完成。对于索引数组,通过foreach遍历效率比for高很多,省去了key->value的查找。count操作直接调用HashTable->NumOfElements,O(1)操作。对于 '123' 这样的字符串,zend会转换为其整数形式。$arr['123']和$arr[123]是等价的。 -资源 +### **资源** + 资源类型变量是PHP中最复杂的一种变量,也是一种复合型结构。 PHP的zval可以表示广泛的数据类型,但是对于自定义的数据类型却很难充分描述。由于没有有效的方式描绘这些复合结构,因此也没有办法对它们使用传统的操作符。要解决这个问题,只需要通过一个本质上任意的标识符(label)引用指针,这种方式被称为资源。 @@ -178,7 +213,8 @@ PHP的zval可以表示广泛的数据类型,但是对于自定义的数据类 资源销毁:资源的数据类型是多种多样的。Zend本身没有办法销毁它。因此需要用户在注册资源的时候提供销毁函数。当unset资源时,zend调用相应的函数完成析构。同时从全局资源表中删除它。 资源可以长期驻留,不只是在所有引用它的变量超出作用域之后,甚至是在一个请求结束了并且新的请求产生之后。这些资源称为持久资源,因为它们贯通SAPI的整个生命周期持续存在,除非特意销毁。很多情况下,持久化资源可以在一定程度上提高性能。比如我们常见的mysql_pconnect ,持久化资源通过pemalloc分配内存,这样在请求结束的时候不会释放。 对zend来说,对两者本身并不区分。 -变量作用域 +### **变量作用域** + PHP中的局部变量和全局变量是如何实现的?对于一个请求,任意时刻PHP都可以看到两个符号表(symbol_table和active_symbol_table),其中前者用来维护全局变量。后者是一个指针,指向当前活动的变量符号表,当程序进入到某个函数中时,zend就会为它分配一个符号表x同时将active_symbol_table指向a。通过这样的方式实现全局、局部变量的区分。 获取变量值:PHP的符号表是通过hash_table实现的,对于每个变量都分配唯一标识,获取的时候根据标识从表中找到相应zval返回。 diff --git a/PHP/README.md b/PHP/README.md index 07b331e..5213624 100644 --- a/PHP/README.md +++ b/PHP/README.md @@ -136,4 +136,5 @@ Fpm是一个实现了Fastcgi协议的程序,用来管理Fastcgi起的进程的, 1. Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module) 2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。 3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。 -4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。 \ No newline at end of file +4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。 + diff --git a/PHP/php7.md b/PHP/php7.md index 0eebc9c..756cbd4 100644 --- a/PHP/php7.md +++ b/PHP/php7.md @@ -35,7 +35,7 @@ if(isset($_GET['a'])) { $a = isset($_GET['a']) ? $_GET['a'] : 'none'; #PHP 7 -$a = isset($_GET['a']) ?? 'none'; +$a = $_GET['a'] ?? 'none'; ``` @@ -50,7 +50,7 @@ function sumOfInts(int ...$ints) { return array_sum($ints); } -ar_dump(sumOfInts(2, '3', 4.1)); // int(9) +var_dump(sumOfInts(2, '3', 4.1)); // int(9) # 严格模式 declare(strict_types=1); @@ -352,10 +352,8 @@ function handler(Throwable $e) { ... } list 会按照原来的顺序进行赋值。不再是逆序了 ```php -list($a,$b,$c) = [1,2,3]; -var_dump($a);//1 -var_dump($b);//2 -var_dump($c);//3 +list($array[], $array[], $array[]) = [1, 2, 3]; +var_dump($array); // [1, 2, 3] ``` list不再支持解开字符串、 @@ -664,3 +662,111 @@ var_dump( ); ``` +## PHP 7.3 + +#### 1. 灵活的heredoc 和nowdoc + +在php 7.3 之前我们定义一大段的字符串。需要用到heredoc + +```php +'1','b'=>'2']; +#php 7.3之前 +$firstKey = key(reset($array)); +# php 7.3 +$firstKey = array_key_first($array);//a +$lastKey = array_key_last($array);//b +``` + +### 6.废除并移除大小写不敏感的常量 + +你可以同时使用大小写敏感和大小写不敏感的常量。但大小写不敏感的常量会在使用中造成一点麻烦。所以,为了解决这个问题,PHP 7.3 废弃了大小写不敏感的常量。 + +原先的情况是: + +- 类常量始终为「大小写敏感」。 +- 使用 `const` 关键字定义的全局常量始终为「大小写敏感」。注意此处仅仅是常量自身的名称,不包含命名空间名的部分,PHP 的命名空间始终为「大小写不敏感」。 +- 使用 `define()` 函数定义的常量默认为「大小写敏感」。 +- 使用 `define()` 函数并将第三个参数设为 `true` 定义的常量为「大小写不敏感」。 + +如今 PHP 7.3 提议废弃并移除以下用法: + +- In PHP 7.3: 废弃使用 `true` 作为 `define()` 的第三个参数。 +- In PHP 7.3: 废弃使用与定义时的大小写不一致的名称,访问大小写不敏感的常量。`true`、`false` 以及 `null` 除外。 \ No newline at end of file diff --git a/README.md b/README.md index 416ef30..35d7eaa 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,71 @@ -## PHP面试准备的资料 +##
PHP面试准备的资料
-这个项目是自己准备面试整理的资料。可能包括PHP、MySQL等资料。方便自己以后查阅,会不定期更新,欢迎提交pr,如果错误,请指出,谢谢。 +这个项目是自己准备面试整理的资料。可能包括PHP、MySQL等资料。方便自己以后查阅,会不定期更新,如果错误,请指出,谢谢。欢迎大家提交PR,谢谢大家的star -- [Linux](https://github.com/xianyunyh/PHP-Interview/tree/master/Linux) +可以通过[https://xianyunyh.gitbooks.io/php-interview/](https://xianyunyh.gitbooks.io/php-interview/)预览。欢迎有精力的朋友完善一下。谢谢。 + +### 目录 + +- [Linux](Linux/REAMDE.md) + - [操作系统简述](操作系统/Readme.md) + - [进程和线程](Linux/进程和线程.md) - [Linux基本命令](https://github.com/xianyunyh/PHP-Interview/blob/master/Linux/Linux%E5%91%BD%E4%BB%A4.md) - - [Linux Crontab](https://github.com/xianyunyh/PHP-Interview/blob/master/Linux/crontab.md) - - [Shell脚本](https://github.com/xianyunyh/PHP-Interview/blob/master/Linux/crontab.md) - - [Linux-Inode介绍](https://github.com/xianyunyh/PHP-Interview/blob/master/Linux/inode.md) - - [VIM编辑器]() - - [Lnmp/Lamp](https://github.com/xianyunyh/PHP-Interview/blob/master/Linux/lanmp.md) + - [Crontab](Linux/crontab.md) + - [Shell](Linux/shell.md) + - [Linux-Inode介绍](Linux/inode.md) + - [VIM编辑器](Linux/Vim.md) + - [Lnmp/Lamp](Linux/lanmp.md) + - [LinuxIO模型.md](Linux/LinuxIO模型.md) -- [数据库]() +- [数据库](Mysql/README.md) - - [MySQL](https://github.com/xianyunyh/PHP-Interview/tree/master/Mysql) + - [MySQL](Mysql/README.md) - - [Mongodb]() + - [Mongodb](MongoDb/MongoDB.md) -- [计算机网络](https://github.com/xianyunyh/PHP-Interview/tree/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C) +- [计算机网络](计算机网络/README.md) - - [IP协议]() + - [IP协议](计算机网络/IP协议.md) - - [TCP协议](https://github.com/xianyunyh/PHP-Interview/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/UDP%E5%8D%8F%E8%AE%AE.md) - - [UDP协议](https://github.com/xianyunyh/PHP-Interview/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/UDP%E5%8D%8F%E8%AE%AE.md) - - [HTTP协议](https://github.com/xianyunyh/PHP-Interview/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/HTTP%E5%8D%8F%E8%AE%AE.md) - - [HTTPS/HTTP2/HTTP](https://github.com/xianyunyh/PHP-Interview/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/HTTP2.md) + - [TCP协议](计算机网络/TCP协议.md) + - [UDP协议](计算机网络/UDP协议.md) + - [HTTP协议](计算机网络/HTTP协议) + - [HTTPS/HTTP2/HTTP](计算机网络/HTTP2.md) -- [版本控制器](https://github.com/xianyunyh/PHP-Interview/tree/master/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E5%99%A8) +- [版本控制器](版本控制器/Git.md) - - [Git](https://github.com/xianyunyh/PHP-Interview/blob/master/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E5%99%A8/Git.md) + - [Git](版本控制器/Git.md) - [SVN]() -- [数据结构](https://github.com/xianyunyh/PHP-Interview/tree/master/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84) - - - [数组]() - - [链表]() - - [单链表]() - - [双链表]() - - [队列]() - - [栈]() - - [堆]() - - [集合]() - - [树]() +- [数据结构](数据结构/README.md) + + - [数组](数据结构/数组.md) + - [链表](数据结构/链表.md) + - [队列](数据结构/队列.md) + - [栈](数据结构/栈.md) + - [堆](数据结构/堆.md) + - [集合](数据结构/集合.md) + - [树](数据结构/树.md) - [二叉树 ]() - [二叉查找树]() - [红黑树]() - [B-Tree、B+Tree]() - [图]() -- [算法]() +- [算法](算法/README.md) - [排序算法]() - - [冒泡排序]() - - [选择排序]() - - [插入排序]() - - [快速排序]() - - [堆排序]() - - [归并排序]() + - [冒泡排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/BubbleSort.php) + - [选择排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/SelectSort.php) + - [插入排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/InsertSort.php) + - [快速排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/QuickSort.php) + - [堆排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/HeapSort.php) + - [归并排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/MergeSort.php) - [查找算法]() - - [二分查找]() + - [二分查找](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Query/BinaryQuery.php) - [hash]() - - [KPM]() + - [KPM](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Query/Kmp.php) - [其他]() - 布隆过滤器 - 贪心算法 @@ -70,31 +75,64 @@ - 最短路径 - 推荐算法 - 深度优先、广度优先 + - [编程之法:面试和算法心得](https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/03.02.html) -- [消息队列]() +- [消息队列](MQ/README.md) - - [RabbitMQ]() + - [RabbitMQ](MQ/rabbitmq.md) - [ActiveMq]() - [Nsq]() - [kafka]() - [缓存系统]() - - [Redis]() + - [Redis](Cache/Redis.md) - [Memcache]() -- [PHP]() +- [PHP](PHP/README.md) + - [PHP7](PHP/php7.md) - [面向对象OOP]() - - [GC]() + - [Zval](https://github.com/xianyunyh/PHP-Interview/blob/master/PHP/PHP-Zval%E7%BB%93%E6%9E%84.md) + - [HashTable](https://github.com/xianyunyh/PHP-Interview/blob/master/PHP/PHP7-HashTable.md) - [Swoole]() -- [设计模式]() +- [设计模式](设计模式/README.md) + +- [面试](面试/README.md) + + +## 生成自己的Gitbook + +```bash +$ npm install gitbook-cli -g +$ git clone https://github.com/xianyunyh/PHP-Interview +$ cd PHP-Interview +$ gitbook serve # 本地预览 +$ gitbook build # 生成静态的html +``` + +## 推荐阅读资料 +- [PHP函数库](http://overapi.com/php) +- [PHP7内核剖析](https://github.com/pangudashu/php7-internal) +- [php7-internal](https://github.com/laruence/php7-internal) +- [PHP7-HashTable](http://nikic.github.io/2014/12/22/PHPs-new-hashtable-implementation.html) +- [PHP7-zval](http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html) +- [PHP中的变化](https://github.com/tpunt/PHP7-Reference) +- [PHP资源集合](https://github.com/ziadoz/awesome-php) +- [设计模式PHP实现](https://github.com/domnikl/DesignPatternsPHP) +- [Swoole](https://www.swoole.com/) +- [程序员的内功-算法和数据结构](http://www.cnblogs.com/jingmoxukong/p/4329079.html) +- [数据结构和算法](http://www.cnblogs.com/skywang12345/p/3603935.html) +- [剑指offer-PHP实现](https://blog.csdn.net/column/details/15795.html) + +## 致谢 + +- [OMGZui](https://github.com/OMGZui) +- [fymmx](https://github.com/fymmx) + + - - [工厂模式]() - - [单例模式]() - - [观察者模式]() - - [适配器模式]() - - [门面模式]() +如果这个系列的文章,对您有所帮助,您可以选择打赏一下作者。谢谢! - ​ +![qrcode](mm_reward_qrcode.jpg) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..a451e4e --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,114 @@ +# Summary + +## LNMP +* [Linux部分](Linux/README.md) + * [Linux基本操作命令](Linux/Linux命令.md) + * [Linux网络相关命令](Linux/Linux命令2.md) + * [Crontab计划任务](Linux/crontab.md) + * [Inode介绍](Linux/inode.md) + * [Shell](Linux/shell.md) + * [Sed命令](Linux/Sed练习.md) + * [Awk命令](Linux/AWK练习.md) + * [IO模型](Linux/LinuxIO模型.md) + * [LAMP/LNMP](Linux/lanmp.md) +* [MySQL部分](Mysql/README.md) + * [SQL语法](Mysql/SQL标准.md) + * [数据库范式](Mysql/MySQL三范式.md) + * [存储引擎](Mysql/存储引擎.md) + * [事务](Mysql/事务.md) + * [索引](Mysql/索引.md) + * [explain分析SQL](Mysql/explain.md) + * [MySQL优化](Mysql/MySQL优化.md) + * [MySQL索引原理及慢查询优化](Mysql/MySQL索引原理及慢查询优化.md) +* [MongoDB](MongoDb/MongoDB.md) +* [PHP](PHP/README.md) + * [PHP7](PHP/php7.md) + * [面向对象OOP](https://github.com/xianyunyh/PHP-Interview/blob/master) + * [Zval结构](PHP/PHP-Zval结构.md) + * [HashTable](PHP/PHP7-HashTable.md) + * [Swoole](https://swoole.com) + * [PHP运行原理](PHP/PHP运行原理.md) + * [正则表达式](PHP/正则表达式.md) + * [PHP-FPM](PHP/PHP-FPM配置选项.md) + +## 操作系统和网络 +* [计算机网络](计算机网络/README.md) + * [IP协议](计算机网络/IP协议.md) + * [TCP协议](计算机网络/TCP协议.md) + * [UDP协议](计算机网络/UDP协议.md) + * [HTTP协议](计算机网络/HTTP协议.md) + * [HTTPS协议](计算机网络/HTTPS.md) + * [HTTP2协议](计算机网络/HTTP2.md) + * [Webscokt](计算机网络/Webscokt.md) +* [版本控制器](版本控制器/Git.md) + * [Git](版本控制器/Git.md) + * SVN + +## 数据结构和算法 +* [数据结构](数据结构/README.md) + * [数组](数据结构/数组.md) + * 链表 + * 单链表 + * 双链表 + * 队列 + * 栈 + * 堆 + * 集合 + * 树 + * 二叉树 + * 二叉查找树 + * 红黑树 + * B-Tree、B+Tree + * [图](https://github.com/xianyunyh/PHP-Interview/blob/master) +* [算法](算法/Readme.md) + * [排序算法](算法/Readme.md) + * [冒泡排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/BubbleSort.php) + * [选择排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/SelectSort.php) + * [插入排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/InsertSort.php) + * [快速排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/QuickSort.php) + * [堆排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/HeapSort.php) + * [归并排序](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Sort/MergeSort.php) + * 查找算法 + * [二分查找](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Query/BinaryQuery.php) + * [hash](https://github.com/xianyunyh/PHP-Interview/blob/master) + * [KPM](https://github.com/PuShaoWei/arithmetic-php/blob/master/package/Query/Kmp.php) + * 其他 + * 布隆过滤器 + * 贪心算法 + * 回溯算法 + * 动态规划 + * 最小生成树 + * 最短路径 + * 推荐算法 + * 深度优先、广度优先 +* [编程之法:面试和算法心得](https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/03.02.html) +* [剑指offer-PHP实现](https://blog.csdn.net/column/details/15795.html) + +## 系统设计和架构 +* [架构和系统设计](架构和系统设计/README.md) +* [消息队列](MQ/README.md) + * [RabbitMQ](MQ/rabbitmq.md) + * ActiveMq + * Nsq + * kafka +* 缓存系统 + * [Redis](Cache/Redis.md) + * Memcache +* [设计模式](设计模式/README.md) + * [创造型](设计模式/Creational.md) + * [行为型](设计模式/Behavioral.md) + * [结构型](设计模式/Structural.md) + * [PHP实现23种设计模式](https://github.com/domnikl/DesignPatternsPHP) + +## 面试 +* [裸辞应对](面试/03裸辞应对.md) +* [写简历](面试/02写简历.md) +* [笔试](面试/笔试题.md) + * [笔试题1](面试/笔试题.md) + * [笔试题2](面试/笔试题2.md) + * [笔试题3](面试/笔试题3.md) + * [笔试题4](面试/笔试题4.md) +* [面试问答](面试/01离职原因回答.md) + * [离职原因](面试/01离职原因回答.md) + * [面试提问](面试/04面试提问.md) + diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..259a24e --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-tactile \ No newline at end of file diff --git a/book.json b/book.json new file mode 100644 index 0000000..9bef177 --- /dev/null +++ b/book.json @@ -0,0 +1,37 @@ +{ + "title": "PHP知识整理", + "description": "PHP知识整理", + "language": "zh-hans", + "extension": null, + "generator": "site", + "gitbook": "3.x.x", + "plugins": [ + "github", + "anchors", + "theme-comscore", + "expandable-chapters-small", + "search-plus", + "-search" + ], + "pluginsConfig": { + "github": { + "url": "https://github.com/xianyunyh/PHP-Interview" + }, + "theme-default": { + "showLevel": true + }, + "search-pro": { + "cutWordLib": "nodejieba", + "defineWord" : ["Gitbook Use"] + } + }, + "links": { + "gitbook": false, + "sharing": { + "google": false, + "facebook": false, + "twitter": false, + "all": false + } + } + } \ No newline at end of file diff --git a/mm_reward_qrcode.jpg b/mm_reward_qrcode.jpg new file mode 100644 index 0000000..6a848e4 Binary files /dev/null and b/mm_reward_qrcode.jpg differ diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" "b/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" new file mode 100644 index 0000000..60290d5 --- /dev/null +++ "b/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" @@ -0,0 +1,73 @@ + +# 操作系统概论 + +操作系统是一种计算机软件。在硬件之上,应用程序之下。主要功能就是管理底下的计算机硬件,并为上层的应用程序提供统一的接口。 + +![img](https://camo.githubusercontent.com/9f8cbe895e7729fc61e18424f2edb7b23ef9d6f3/687474703a2f2f6974666973682e6e65742f486f6d652f4d6f64756c65732f496d616765732f6974666973685f34343836395f302e6a7067) + +**系统调用**:应用程序直接调用操作系统提供的接口 如write 函数 + +**库函数调用**:应用程序通过一些库函数直接调用 如 fwrite + +## 内核态和用户态 + +操作系统为了管理内存。将内存分为**内核空间**(内核态)和**用户空间**。内存空间和用户空间之间有隔离。 + +**用户空间即上层应用程序的活动空间**,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用 + + + + + +![img](https://images2015.cnblogs.com/blog/431521/201605/431521-20160523163606881-813374140.png) + +内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。 + +shell就是外壳。类似一种胶水的功能。可以通过shell访问内核。 + +内核态与用户态是指CPU的运行状态(即特权级别),每个进程的每种CPU状态都有其运行上下文,运行上下文就包括了当前状态所使用的空间,CPU访问的逻辑地址(即空间)通过地址映射表映射到相应的物理地址(即物理内存)。在Linux系统中,进程的用户空间是独立的,而内核空间是公用的,进程切换时,用户空间切换,内核空间不变。 + +对于多数CPU而言,处于内核态时,可以访问所有地址空间,而处于用户态时,就只能访问用户空间了。 + + + +## 用户态和内核态切换 + +操作系统的资源是有限的,如果访问资源的操作过多,必然会消耗过多的资源,而且如果不对这些操作加以区分,很可能造成资源访问的冲突。 + +为了减少有限资源的访问和使用冲突,Unix/Linux的设计哲学之一就是:对不同的操作赋予不同的执行等级,就是所谓特权的概念 + +Linux操作系统中主要采用了0和3两个特权级,分别对应的就是内核态和用户态。运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。很多程序开始时运行于用户态,但在执行的过程中,一些操作需要在内核权限下才能执行,这就涉及到一个从用户态切换到内核态的过程 + +![img](https://images2015.cnblogs.com/blog/431521/201605/431521-20160523210140147-1668637440.gif) + + + +## 库函数调用和系统调用的区别 + +![这里写图片描述](http://img.blog.csdn.net/20170117211419709?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTEZfMjAxNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +系统调用(英语:system call),指运行在用户空间的应用程序向操作系统内核请求某些服务的调用过程。 系统调用提供了用户程序与操作系统之间的接口。一般来说,系统调用都在内核态执行。由于系统调用不考虑平台差异性,由内核直接提供,因而移植性较差(几乎无移植性)。 + +库函数(library function),是由用户或组织自己开发的,具有一定功能的函数集合,一般具有较好平台移植性,通过库文件(静态库或动态库)向程序员提供功能性调用。程序员无需关心平台差异,由库来屏蔽平台差异性。 + +| 函数库调用 | 系统调用 | +| ----------------------------- | ----------------------- | +| 平台移植性好 | 依赖于内核,不保证移植性 | +| 调用函数库中的一段程序(或函数) | 调用系统内核的服务 | +| 一个普通功能函数的调用 | 是操作系统的一个入口点 | +| 在**用户空间**执行 | 在**内核空间**执行 | +| 它的运行时间属于“用户时间” | 它的运行时间属于“系统”时间 | +| 属于过程调用,调用开销较小 | 在用户空间和内核上下文环境间切换,开销较大 | +| 库函数数量较多 | UNIX中大约有90个系统调用,较少 | +| 典型的C函数库调用:printf scanf malloc | 典型的系统调用:fork open write | + +![clip_image002](http://blog.chinaunix.net/attachment/201207/11/27105712_13419741441gpp.gif) + +读写IO通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),使用库函数调用可以大大减少系统调用的次数。这是因为缓冲区技术。在用户空间和内核空间,对文件操作都使用了缓冲区,当内核缓冲区写满之后或写结束之后才将内核缓冲区内容写到文件对应的硬件媒介中。 + +**不带缓冲指的是每个read和write这些文件I/O操作都调用的是系统调用,属于内核态的操作** + +诸如fread和fwrite这些标准I/O操作属于用户态操作,具体是库函数的实现,需要借助用户缓冲区来实现 + +更多内容可以参考 [操作系统](https://github.com/xianyunyh/studynotes/tree/master/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F) \ No newline at end of file diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" "b/\346\223\215\344\275\234\347\263\273\347\273\237/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" new file mode 100644 index 0000000..7d4777d --- /dev/null +++ "b/\346\223\215\344\275\234\347\263\273\347\273\237/\350\277\233\347\250\213\345\222\214\347\272\277\347\250\213.md" @@ -0,0 +1,9 @@ +## 进程和线程的区别 + +进程是一次程序运行的活动。进程有自己的pid,堆栈空间等资源。 + +线程是进程里面的一个实体。是CPU调度的基本单位。它比进程更小。线程本身不拥有系统资源。只拥有自己的程序计数器、堆栈、寄存器。和同一个进程中的其他线程共享进程中的内存。 + +线程开销小。进程切换开销比较大。进程切换,上下文。 + +进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。 \ No newline at end of file diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" new file mode 100644 index 0000000..e31bb76 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" @@ -0,0 +1,625 @@ +## :pencil2:Leetcode经典二叉树题目集合 + +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +**** +### :pencil2:1.二叉树的前序遍历(leetcode144) + + +​ + + +**前序遍历,先访问根结点,然后在访问左子树,最后访问右子树。可以利用栈的特点,这里我结合了队列和栈的特点来实现。先压入树,取出根节点。先把根节点值push到队列中,然后把右子树压入栈中,最后压入左子树。返回队列。当然你可以调整成你想要的实现方式。(只要前中后序顺序理解正确即可)** + +```php + +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ + +class Solution { + + /** + * @param TreeNode $root + * @return Integer[] + */ + function preorderTraversal($root) { + $res=[]; + $list=[]; + array_unshift($res,$root); + while(!empty($res)){ + $current=array_shift($res); + if($current==null) continue; + array_push($list,$current->val); + array_unshift($res,$current->right); + array_unshift($res,$current->left); + } + return $list; + } +} + +``` + +**** + +### :pencil2:2.二叉树的中序遍历(leetcode94) + + +​ + + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function inorderTraversal($root) { + $res=[]; + $this->helper($root,$res); + return $res; + } + function helper($root,&$res){ + if($root !=null){ + if($root->left !=null) $this->helper($root->left,$res); + array_push($res,$root->val); + if($root->right !=null) $this->helper($root->right,$res); + } + + } +``` + +**或者不用递归** + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function inorderTraversal($root) { + $res=[]; + $list=[]; + while(!empty($list) || $root !=null){ + while($root != null){ + array_unshift($list,$root); + $root=$root->left; + } + $root=array_shift($list); + array_push($res,$root->val); + $root=$root->right; + } + return $res; + } +``` +**** + + +### :pencil2:3.二叉树的后序遍历(leetcode145) + + +​ + + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function postorderTraversal($root) { + $list=[]; + $res=[]; + array_push($list,$root); + while(!empty($list)){ + $node=array_shift($list); + if(!$node) continue; + array_unshift($res,$node->val); + array_unshift($list,$node->left); + array_unshift($list,$node->right); + } + return $res; + } + } +``` +**** + +### :pencil2:4.二叉树的层次遍历(leetcode102) + + +​ + + +**DFS和BFS都可以解,竟然已经要我们按照层打印了,那么先使用BFS,思路就是先判断树是否是空,不是空加入一个队列的结构中,如果队列不为空,取出头元素,那么当前元素表示的就是当前这一层了,所以只需要遍历这一层里的所有的元素即可,然后下一层....** + +```php + class Solution { + + /** + * @param TreeNode $root + * @return Integer[][] + */ + function levelOrder($root) { + if(empty($root)) return []; + $result = []; + $queue = []; + array_push($queue,$root); + while(!empty($queue)){ + $count = count($queue); + $leveQueue = []; + for($i = 0;$i<$count;$i++){ + $node = array_shift($queue); + array_push($leveQueue,$node->val); + if($node->left) array_push($queue,$node->left); + if($node->right) array_push($queue,$node->right); + } + array_push($result,$leveQueue); + } + return $result; + } + } +``` + +**如果使用DFS的话,就是一条路走到黑,然后再重新一路路的退回来再找下一路,所以这样的话,每一次我们需要记录一下当前他所在的这个点属于哪一层即可,代码用递归实现。** +```php + +class Solution { + + /** + * @param TreeNode $root + * @return Integer[][] + */ + function levelOrder($root) { + if(empty($root)) return []; + $result=[]; + $this->helper($result,$root,0); + return $result; + } + + function helper(&$result,$node,$level){ + if(empty($node)) return ; + if(count($result)<$level+1){ + array_push($result,[]); //说明当前行没有结果 + } + array_push($result[$level],$node->val); + $this->helper($result,$node->left,$level+1); + $this->helper($result,$node->right,$level+1); + } +} + +``` +**** + +### :pencil2:5.二叉树的最大深度(leetcode104) + + +​ + + +**DFS和BFS都可以解,竟然已经要我们按照层打印了,那么先使用BFS,思路就是先判断树是否是空,不是空加入一个队列的结构中,如果队列不为空,取出头元素,那么当前元素表示的就是当前这一层了,所以只需要遍历这一层里的所有的元素即可,然后下一层....** + +```php + + /** + * @param TreeNode $root + * @return Integer + */ + function maxDepth($root) { + if(empty($root)) return 0; + $left = $this->maxDepth($root->left); + $right = $this->maxDepth($root->right); + return $left<$right? $right+1:$left+1; + return max($left,$right)+1; + } +``` +**** + +### :pencil2:6.二叉树的最小深度(leetcode111) + + +​ + + +**DFS和BFS都可以求解** + +```php + + //BFS + /** + * @param TreeNode $root + * @return Integer + */ + function minDepth($root) { + if(empty($root)) return 0; + if(!$root->right) return $this->minDepth($root->left)+1; + if(!$root->left) return $this->minDepth($root->right)+1; + $left=$this->minDepth($root->left); + $right=$this->minDepth($root->right); + return min($left,$right)+1; + + } +``` + +```php + +//DFS + /** + * @param TreeNode $root + * @return Integer + */ + function minDepth($root) { + if(empty($root)) return 0; + $left=$this->minDepth($root->left); + $right=$this->minDepth($root->right); + if($left==0 || $right==0) return $left+$right+1; + return min($left,$right)+1; + } +``` +**** + + +### :pencil2:7.判断是否是平衡二叉树(leetcode110) + + +​ + + +**每一节点的两个子树的深度相差不能超过1。如果是空树,直接true。** + +```php + + +class Solution { + + /** + * @param TreeNode $root + * @return Boolean + */ + private $result=true; + function isBalanced($root) { + if(empty($root)) return true; + $this->helper($root); + return $this->result; + } + function helper($root) +{ + if(!$root) return ; + $left=$this->helper($root->left); + $right=$this->helper($root->right); + if(abs($left-$right)>1) $this->result=false; + return max($left,$right)+1; + } +} +``` +**** + + +### :pencil2:8.判断是否是对称二叉树(leetcode101) + + +​ + + +**1.两个子节点都是空,那说明他们是对称的返回true** + +**2.一个子节点为空,另一个子节点不为空,false** + +**3.两个子节点都不为空,但是他们不相等,false** + +**4.两个子节点不为空且相等,继续判断他们的左子树和右子树,把左子树的左子节点和右子树的右子节点进行比较,把左子树的右子节点和右子树的左子节点进行比较** + +```php + /** + * @param TreeNode $root + * @return Boolean + */ + function isSymmetric($root) { + if(empty($root)) return true; + return $this->helper($root->left,$root->right); + } + function helper($l,$r){ + if(!$l && !$r) return true; + if(!$l || !$r || $l->val != $r->val) return false; + return $this->helper($l->left ,$r->right) && $this->helper($l->right,$r->left); + } +``` +**** + +### :pencil2:9.反转二叉树(leetcode226) + + +​ + + +```php + + /** + * @param TreeNode $root + * @return TreeNode + */ + function invertTree($root) { + if(!$root) return null; + $list=[]; + array_push($list,$root); + while(!empty($list)){ + $node=array_shift($list); + $temp=$node->left; + $node->left=$node->right; + $node->right=$temp; + if($node->left) array_push($list,$node->left); + if($node->right) array_push($list,$node->right); + } + return $root; + } +``` +**递归解** +```php + /** + * @param TreeNode $root + * @return TreeNode + */ + function invertTree($root) { + if(empty($root)){ + return null; + } + $right=$this->invertTree($root->right); + $left=$this->invertTree($root->left); + $root->left=$right; + $root->right=$left; + return $root; + } +``` +**** + + +### :pencil2:10.给定单链表(值有序)转化成平衡二叉查找树(leetcode109) + + +​ + + +**先将链表数据转换成有序数组,然后利用二分查找的特性,构建左右子树。** + +```php + +/** + * Definition for a singly-linked list. + * class ListNode { + * public $val = 0; + * public $next = null; + * function __construct($val) { $this->val = $val; } + * } + */ +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ +class Solution { + + /** + * @param ListNode $head + * @return TreeNode + */ + function sortedListToBST($head) { + $data=[]; + while($head){ + array_push($data,$head->val); + $head=$head->next; + } + return $this->helper($data); + } + + function helper($data) +{ + if(!$data) return ; + $middle=floor(count($data)/2); + $node=new TreeNode($data[$middle]); + $node->left=$this->helper(array_slice($data,0,$middle)); + $node->right=$this->helper(array_slice($data,$middle+1)); + return $node; + } +} +``` +**** + + +### :pencil2:11.强盗打劫版本3(leetcode337) + + +​ + + +**最后的目的算出最多能抢金额数而不触发报警器。除了根节点,每一个结点只有一个父节点,能直接相连的两个节点不能同时抢,比如图1,抢了根节点,直接相连的左右子结点就不能抢。所以要么抢根节点的左右子结点,要么根结点+根结点->left->right+根结点->right->right。** + + +```php + +//递归 +/** + * @param TreeNode $root + * @return Integer + */ + function rob($root) { + if($root==null){ + return 0; + } + $res1=$root->val; + if($root->left !=null) { + $res1 +=$this->rob($root->left->left)+$this->rob($root->left->right); + } + if($root->right !=null){ + $res1 +=$this->rob($root->right->left)+$this->rob($root->right->right); + } + + $res2=$this->rob($root->left)+$this->rob($root->right); + return max($res1,$res2); + + } +``` + +**上面那种大量的重复计算,改进一下。** + + **如果结点不存在直接返回0,对左右结点分别递归,设置了4个变量,ll和lr分别表示左子结点的左右子结点的最大金额数,rl和rr分别表示右子结点的左右子结点的最大金额数。所以我们最后比较的还是两种情况,第一种就是当前结点+左右子结点的左右子结点的值(即这里定义的ll,lr,rl,rr).第二种是当前结点的左右子结点的值(也就是说我只抢当前结点的子结点,不抢当前结点和孙子结点),再通俗的说就是如果树的层数是3层,要么抢中间一层,要么抢上下两层,谁钱多抢谁。** + + ```php +/** + * @param TreeNode $root + * @return Integer + */ + function rob($root) { + $l=0;$r=0; + return $this->countMax($root,$l,$r); + } + function countMax($root,&$l,&$r){ + if($root==null) return 0; + $ll=0;$lr=0;$rl=0;$rr=0; + $l=$this->countMax($root->left,$ll,$lr); + $r=$this->countMax($root->right,$rl,$rr); + return max($root->val+$ll+$lr+$rl+$rr,$l+$r); + } +``` +**** + +### :pencil2:12.判断二叉树路径和是否存在(leetcode112) + + +​ + + +**只要使用深度优先算法思想遍历每一条完整的路径,如果是个空树直接false,如果结点没有左右子树(说明此时已然是叶子结点,判断值是否是给定值,这个条件正好是递归终止的条件),相等直接返回true,根据这个推导出递归公式。** + +```php +/** + * @param TreeNode $root + * @param Integer $sum + * @return Boolean + */ + function hasPathSum($root, $sum) { + if($root==null){ + return false; + } + if($root->left ==null && $root->right==null && $root->val==$sum) return true; + return $this->hasPathSum($root->left,$sum-$root->val) || $this->hasPathSum($root->right,$sum-$root->val); + } +``` + +**改成迭代** + +```php + +/** + * @param TreeNode $root + * @param Integer $sum + * @return Boolean + */ + function hasPathSum($root, $sum) { + if($root==null){ + return false; + } + $res=[]; + array_push($res,$root); + while(!empty($res)){ + $node=array_shift($res); + if(!$node->left && !$node->right ){ + if($node->val==$sum) return true; + } + if($node->left){ + $node->left->val +=$node->val; + array_push($res,$node->left); + } + if($node->right){ + $node->right->val +=$node->val; + array_push($res,$node->right); + } + } + return false; + } +``` +**** + + +### :pencil2:13.判断是否是二叉查找树(leetcode98) + + +​ + + +**思路有两种,二叉查找树的特点就是左子树上的结点都小于根结点,右子树上的结点都大于根节点。所以有两个方向,可以分别递归的判断左子树,右子树。或者拿左子树上的最大值,右子树上的最小值分别对应根结点进行判断。** + +```php + +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ +class Solution { + + /** + * @param TreeNode $root + * @return Boolean + */ + function isValidBST($root) { + return $this->helper($root,null,null); + } + function helper($root,$lower,$upper){ + if($root==null) return true; + $res=$root->val; + if($lower !==null && $res<=$lower) return false; + if($upper !==null && $res>=$upper) return false; + if(!$this->helper($root->left,$lower,$res)) return false; + if(!$this->helper($root->right,$res,$upper)) return false; + return true; + } +} +``` +**** + + +### :pencil2:14.找出二叉树最后一层最左边的值(leetcode513) + + +​ + + +**思路有两种,二叉查找树的特点就是左子树上的结点都小于根结点,右子树上的结点都大于根节点。所以有两个方向,可以分别递归的判断左子树,右子树。或者拿左子树上的最大值,右子树上的最小值分别对应根结点进行判断。** + +```php + /** + * @param TreeNode $root + * @return Integer + */ + function findBottomLeftValue($root) { + $data=[]; + array_push($data,$root); + while(!empty($data)){ + $node = array_shift($data); + if($node->right) array_push($data,$node->right); + if($node->left) array_push($data,$node->left); + } + return $node->val; + } +``` +**** + +### 联系 + + +​ + + + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" new file mode 100644 index 0000000..766ea8e --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" @@ -0,0 +1,228 @@ +## :数据结构线性表之二叉树1 +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + + +​ + + +### :pencil2:一.二叉树的定义 +**既然都叫二叉树了,那么二叉树的特点是每个至多只有两棵子树。换句话来说,每个结点的度不超过两个,并且二叉树的子树还存在左右之分,它的次序不能任意颠倒。** +### :pencil2:二叉树的表现形态 + + +​ + + +**1.满二叉树:棵深度为k有且有2^k-1结点的树称之为满二叉树,比如图中的a(深度为4,拥有15个结点)。这里我们就可以得到二叉树的一个特性:在二叉树的第i层中至多有2^i-1个结点(i>=1)。当然了他不止这一个特性。。。** + +**2.完全二叉树:深度为k,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n一一对应时,才是完全二叉树。这里图c不是完全二叉树,假设根节点A是1,那么图c在n等于12这个点上就没有对应。所以不是完全二叉树。所以完全二叉树的特点:(1)叶子结点只可能在层次最大的两层上出现。(2)对任意一个结点,若其右子树的最大层次是l,那么其左子树的最大层数必须是l或者l+1。** + +**三·二叉树的存储树这种结构的存储不像线性结构那样简单,并不能直接用前后关系来描述。一个树有有很多的子树,一个结点不止一个后继。下面用图来说明用数组存储和链表存储。** + +### :pencil2:三.二叉树的存储 + + +​ + + +**按照顺序存储的结构。用一组连续的存储单元至上而下,至左到右存储,图中的0表示不存在该结点,每一个不存在的点我们都需要浪费一个空间。所以在最坏的情况下,一个深度为k且只有k个结点的单支树(树中不存在度为2的结点)却需要长度为2^k-1的一维数组。所以这种顺序存储适合完全二叉树。** + + +​ + + +**链式存储的结构是由一个数据元素和分别指向其左右子树的两个分支构成。包含了三个域:数据域,左右指针域。链表的头指向树的根节点。** + +### :pencil2:四·二叉树的前中后序遍历 +```php + +//二叉树 +class Tree{ + public $res=''; + public $left=null; + public $right=null; + public function __construct($res) +{ + $this->res=$res; + } + +} +//前序遍历 + function front($tree){ + if($tree==null){ + return ; + } + echo $tree->res.'
'; + front($tree->left); + front($tree->right); +} + +//中序遍历 + function middle($tree) +{ + if($tree==null){ + return ; + } + middle($tree->left); + echo $tree->res.'
'; + middle($tree->right); + } + +//后序遍历 + function backend($tree) +{ + if($tree==null){ + return ; + } + backend($tree->right); + backend($tree->left); + echo $tree->res.'
'; +} + +$treeA=new Tree('a'); +$treeB=new Tree('b'); +$treeC=new Tree('c'); +$treeA->left=$treeB; +$treeA->right=$treeC; +// +//front($treeA); +//echo "-------------------".'
'; +//middle($treeA); +//echo "-------------------".'
'; +//backend($treeA); +``` + +### :pencil2:五.二叉排序树的插入,查找,删除. +**我主要说明一下删除操作,删除操作主要分三种情况:(1)如果待删除结点中,只有一个左子结点,那么只要把待删除结点的父结点的left指向待删除结点的左子结点。(2)如果待删除结点只有一个右子结点,那么只要把带删除结点的父结点的right指向带删除结点的右子结点。(3)如果待删除结点有左右结点,那么需要找到这个结点右子树中最小的结点,把他替换到待删除的结点上面。** + + + ​ + + +```php + +class Tree{ + public $res=''; + public $left=null; + public $right=null; + public function __construct($res) +{ + $this->res=$res; + } + +} + +class BinarySortTree +{ + public $tree; + + public function getTree() +{ + return $this->tree; + } + + //插入 + public function insertTree($data) + { + if(!$this->tree){ + $this->tree=new Tree($data); + return ; + } + $p=$this->tree; + while($p){ + if($data<$p->res){ //如果插入结点当前结点 + if(!$p->left){ //并且不存在左子结点 + $p->left=new Tree($data); + return ; + } + $p=$p->left; + }elseif ($data>$p->res){ + if(!$p->right){ + $p->right=new Tree($data); + return ; + } + $p=$p->right; + }else{ + return ; + } + } + } + + + + //删除 + public function deleteTree($data) +{ + if (!$this->tree) { + return; + } + $p = $this->tree; + $fatherP = null; + while ($p && $p->res !== $data) { + $fatherP=$p; //结点的父结点 + if ($data > $p->res) { + $p = $p->right; + }else{ + $p=$p->left; + } + } + + //如果二叉树不存在 + if($p==null){ + var_dump('当前树中没有此结点');return; + } + + //待删除待有两个子结点 + if($p->left && $p->right){ + $minR=$p->right; + $minRR=$p;// 最小结点的父结点 + //查找右子树的最小结点 + while($minR->left){ + $minRR=$minR; + $minR=$minR->left; + } + $p->res=$minR->res;//把右子树上最小结点的值赋值给待删除结点 + $p=$minR; + $fatherP=$minRR; + + } + $child=null; + if($p->left){ + $child=$p->left; + }elseif($p->right){ + $child=$p->right; + }else{ + $child=null; + } + + if(!$fatherP){ //待删除结点是根结点 + $this->tree=$child; + }elseif ($fatherP->left==$p){ //待删除结点只有一个左结点,把待删除结点的父结点的left指向待删除结点的子节点 + $fatherP->left=$child; + }else{ //待删除结点只有一个右结点,把待删除结点的父结点的right指向待删除结点的子节点 + $fatherP->right=$child; + } + + } +} + +$sortTree=new BinarySortTree(); +$sortTree->insertTree(9); +$sortTree->insertTree(8); +$sortTree->insertTree(10); +$sortTree->insertTree(5); +$sortTree->insertTree(6); +$sortTree->insertTree(4); +//$sortTree->search(1); +//$sortTree->deleteTree(8); +//var_dump($sortTree->getTree()); +``` + +### 联系 + + +​ + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" new file mode 100644 index 0000000..42ed110 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" @@ -0,0 +1,115 @@ +## :数据结构线性表之堆栈 + +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + + +​ + + +### :pencil2:一.栈的特点 +**栈是仅限在栈顶进行插入和删除的线性表.对栈来说,表尾端称之为栈顶,表头端称之为栈底.假设栈S=(a1,a2,a3,a4,a5....an),那么a1为栈的栈底,an为栈顶元素.对栈的操作是按照后进先出的原则,因此栈又称之为(LIFO)数据结构.** + +### :pencil2:二.栈的表示和实现 +**和线性表类似,栈也有两种存储的方式.顺序栈,即栈的存储是利用一组连续的存储单元依次存放自栈底到栈顶的元素.同时还要定义指针top指向栈顶元素在顺序栈中的位置.通常的做法是top=0表示空栈.一般在使用栈的过程中难以预料所需要的空间大小.所以预先分配大小的容量,当栈的使用空间不够时,再进行动态的扩容.top作为栈顶的指针,初始的时候指向栈底,当有元素入栈时,top自增,当从栈中删除一个元素时,top--,然后删除元素,所以top始终指向栈顶的下一个空位置.** + ```php +LOC(ai+1)=LOC(ai)+L //申明 这里的i,i+1是下标 +``` +**线性结构的顺序表示以元素在计算机中的物理位置相邻来表示逻辑位置相邻,相邻两个元素之间的位置以物理位置的角度分析就是相差一位,只要确定了存储线性表的起始位,那么我们就能任意存储线性表中元素,下图具体表示** + + +​ + + +### :pencil2:三.栈的使用场景 +**栈的使用场景很多,例如idea的撤销回退功能,浏览器前进和后退功能.数制的转换,表达式求值,八皇后.......** +### :pencil2:四.用栈实现代码 +```php + +/** + * 使用栈实现十进制转8精制,其他转换类似 + */ +function tenChageEight($num) +{ + $data=[]; + while($num) { + $val = $num % 8; + $num = intval(floor($num / 8)); + array_unshift($data, $val); + } + $data2=[]; + while(!empty($data)){ + array_push($data2,array_shift($data)); + } + return implode($data2); +} +var_dump(tenChageEight(1348)); +``` +**** +```php + +class Stack +{ + private $stack=[]; + private $size=0; + public function __construct($size) +{ + $this->size=$size; + } + + /** + *推入栈顶 + */ + public function push($value) +{ + if(count($this->stack)>=$this->size){ + return false; + } + array_unshift($this->stack,$value); + + } + /** + *出栈 + */ + public function pop() +{ + if($this->size==0){ + return false; + } + array_shift($this->stack); + } + /** + *获取栈顶元素 + */ + public function top() +{ + if($this->size==0){ + return false; + } + return current($this->stack); + } + + public function data() +{ + return $this->stack; + } + +} + +$stack=new Stack(10); +$stack->push(2); +$stack->push(10); +$stack->push(8); +$stack->pop(); +var_dump($stack->top()); +var_dump($stack->data()); +``` +### 联系 + + +​ + + + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000..6040c2d --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,65 @@ +##旋转字符串 +给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。 +###解法一:暴力移位法 +```php +function move(&$str , $n){ + for ($i = 0; $i<$n; $i++){ + LeftShiftOne($str); + } +} + +function LeftShiftOne(&$str){ + $len = strlen($str); + $last = $str[$len - 1]; + for ($i = 0; $i<$len-1; $i++){ + $str[$i - 1] =$str[$i]; + } + $str[$len-2] = $last; +} +$str = "abcd"; +move($str, 4); + +``` +时间复杂度为O(m n),空间复杂度为O(1),空间复杂度符合题目要求,但时间复杂度不符合,所以,我们得需要寻找其他更好的办法来降低时间复杂度。 +###解法二:三步反转法 +对于这个问题,换一个角度思考一下。 +将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。 +例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可: +- 首先将原字符串分为两个部分,即X:abc,Y:def; +- 将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。 +- 反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。 +```php + +/** + * 旋转字符串 + * @param $str + * @param $start + * @param $end + */ +function ReverseString(&$str, $start, $end){ + while($start < $end){ + $t = $str[$start]; + $str[$start++] = $str[$end]; + $str[$end--] = $t; + } +} + + +/** + * @param $str + * @param $n 字符串长度 + * @param $m 移动位数 + */ +function LeftRotateString(&$str, $n, $m){ + $m %= $n; + ReverseString($str, 0, $m-1); + ReverseString($str, $m, $n-1); + ReverseString($str, 0, $n-1); + +} + +$str = "abcd"; +LeftRotateString($str, strlen($str), 1); +``` + +这就是把字符串分为两个部分,先各自反转再整体反转的方法,时间复杂度为O(n),空间复杂度为O(1),达到了题目的要求。 \ No newline at end of file diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" new file mode 100644 index 0000000..5174d2b --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" @@ -0,0 +1,114 @@ +## 数据结构之散列表 +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +​ + + +### :pencil2:一.散列表的介绍 +**散列表也叫哈希表,在前面的各种数据结构中(数组,链表,树等),记录在结构中的相对位置是随机的,和记录的关键字不存在确定的关系。在顺序结构中,顺序查找,比较的结果是'='或者'!=',在二分查找,二叉排序树比较的关系是'>','=‘,’<‘,查找的效率依赖于查找过程中比较的次数。** +**什么样的方法能使得理想情况下不需要进行任何比较,通过一次存储就能得到所查的记录。答案就是记录存储位置和关键字之间的对应关系。** + + +​ + + +**我们记录了存储位置和关键字的对应关系h,因此在查找的过程中,只要根据对应关系h找到给定值k的像h(k),如果结构中存在关建字和k相等的记录,那么肯定是存储在h(k)的位置上,就像上图k1..k5的一样,我们称这个对应关系h为散列函数或者是哈希函数。** +**** +**从上面的例子可以看出:** + +**1.散列函数是一个映像,因此设定散列函数可以非常灵活。** + +**2.从图中可以看出,k2 != k5 ,但是h(k2)===h(k5),这就是散列冲突。** + +### :pencil2:二.散列冲突 + + +​ + + +**我们看当前例子,关键字 John Smith !=Sandra Dee,但是通过散列函数之后h(John Smith)===h(Sandra Dee),他们的位置都在散列表01这个位置,但是这个位置只能存储一个记录,那多出来的记录存放在哪里?** + +**正因为散列函数是一个映像,我们在构造散列函数的时候,只能去分析关键字的特性,选择一个恰当的散列函数来避免冲突的发生。一般情况下,冲突只能尽可能的减少,而不能完全避免。** + +### :pencil2:三.散列函数的构造 +**先来看看什么是好的散列函数。如果对于关键字集合中任意一个,经过散列函数映射到地址集合中任何一个位置的概率都是相等的,就可以称为均匀散列函数。** + +**1.直接定址法** + +**取关键字或者关键字的某个线性函数值为哈希地址。** + +**h(key)=key或者h(key)=a*key+b a,b为常量。比如说做一个地区年龄段的人数统计,我们就可以把年龄1-100作为关键字,现在你要查38岁的人口数时只需要h(38)=???。** +**** + +**2.数字分析法** + +**要通过具体的数字分析,找出关键字集合的特点和关系,通过这组关系构造一个散列函数。** +**** +**3.平方取中法** + +**取关键字平方后的中间几位为哈希地址,因为一般情况下,选定哈希函数时并不一定知道关键字的全部情况,取哪几位也不一定合适,而一个平方后的中间几位数和数的每一位都有关,由此使随机分布的关键字得到的哈希地址也是随机的。** +**** +**4.折叠法** + +**将关键字分割成位数相同的几部分,然后取这几部分的叠加和,也就是舍去它的进位,作为他的哈希地址。** +**** +**5.除留余数法** +**取关键字被某个不大于散列表表长m的数(暂且称之为p)除后所得余数为哈希地址。** +```php +h(key)=key MOD p, p<=m +``` +**以上都偏于理论,具体的使用需要视情况而定,通常,考虑的因素有以下几点** + +**1.计算散列函数所需的时间** + +**2.关键字的长度** + +**3.哈希表的大小** + +**4.关键字的分布情况** + + **5.记录查找的频率** + + ### :pencil2:四.处理冲突 + **之前说过,散列函数可以减少冲突,但是不能避免,这时候我们就需要处理冲突。** + + **1.开放寻址法** + + **简单的来说,下图中当前哈希表的长度是8,现在已填入关键字1,9,3,4,5的记录,假如当前我们需要再填入关键字10这条记录。通过散列函数得出h(3)===h(10),但是此时哈希地址2已被3占领。现在我们咋么处理?** + + **线性探测再散列得到下一个地址3,发现3也被占领,再求下一个地址4,还被占领,直到5这个位置时,处理冲突过程结束,把当前h(10)记录在地址为5的位置即可。也可以使用二次探测再散列或者伪随机再散列都是属于开放寻址法。** + + + ​ + + +**** + +**2.再散列法** + +**即在同义词产生地址冲突时计算另一个散列函数的地址,直到冲突不再发生,这种方法毋庸置疑,增加了计算的时间。** +**** +**3.链地址法** + +**将所有关键字为同义词的记录存储在同一个线性链表中。初始状态是一个空指针。凡是通过关键字计算出地址一样的记录都插入到当前位置的链表中,在链表中插入的位置可以是表头,表尾,或者表中,以保持同义词在同一个线性表中按关键字有序。** + + + ​ + + + **** + + **4.建立一个公共溢出区** + + **假设散列函数的值域为[0,m-1],则设置向量HashTable[0,m-1]为基本表,每一个分量存储一个记录,另外设置向量OverTable[0,v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表中。** +**** + ### 联系 + + + ​ + + + + + + diff --git "a/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/API\350\256\276\350\256\241.md" "b/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/API\350\256\276\350\256\241.md" new file mode 100644 index 0000000..e99b89e --- /dev/null +++ "b/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/API\350\256\276\350\256\241.md" @@ -0,0 +1,89 @@ +## REST API + +REST(Representational State Transfer)表述性状态转换,**REST指的是一组架构约束条件和原则** 。 + +使用URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。 + +### 基本概念 + +- 资源 + + > 资源就是网络上的一个实体,一段文本,一张图片或者一首歌曲。资源总是要通过一种载体来反应它的内容。文本可以用TXT,也可以用HTML或者XML、图片可以用JPG格式或者PNG格式,JSON是现在最常用的资源表现形式。 + +- 统一资源接口 + + > 统一接口。RESTful风格的数据元操CRUD(create,read,update,delete)分别对应HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口。 + + - GET 获取资源 + - PUT更新资源 + - POST 新增资源 + - DELETE 删除 + +- URI + + > URI。可以用一个URI(统一资源定位符)指向资源,即每个URI都对应一个特定的资源。要获取这个资源访问它的URI就可以,因此URI就成了每一个资源的地址或识别符。一般的,每个资源至少有一个URI与之对应,最典型的URI就是URL。 + +- 无状态 + + > 所谓无状态即所有的资源都可以URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而变化。有状态和无状态的区别,举个例子说明一下,例如要查询员工工资的步骤为第一步:登录系统。第二步:进入查询工资的页面。第三步:搜索该员工。第四步:点击姓名查看工资。这样的操作流程就是有状态的,查询工资的每一个步骤都依赖于前一个步骤,只要前置操作不成功,后续操作就无法执行。如果输入一个URL就可以得到指定员工的工资,则这种情况就是无状态的,因为获取工资不依赖于其他资源或状态,且这种情况下,员工工资是一个资源,由一个URL与之对应可以通过HTTP中的GET方法得到资源,这就是典型的RESTful风格。 + + + +### 设计风格 + +- 协议 + + API接口通讯,一般是通过HTTP[s]协议。 + +- 域名 + + 域名应单独部署到对应的域名。 + + ```php + api.github.com + ``` + +- 版本控制 + + ``` + api.github.com/v1/ + ``` + +- 路径规则 + + 路径中,不要出现动词。比如getUsers。复数表示获取集合数组 + + ``` + /v1/user/10 获取id为10的用户 + /v1/users 获取所有用户 + ``` + +- HTTP请求方式表示动作 + + - GET 表示获取资源 + - PUT 更新资源 + - POST新增资源 + - DELETE 删除资源 + + ``` + GET /users + PUT /user/10 + POST /user/10 + DELETE /user/10 + ``` + +- 过滤信息 + + 如果记录过多,可以使用分页过滤信息 + + - ?limit=10 指定返回记录的数量 + - ?offset=10:指定返回记录的开始位置。 + - ?page=2&per_page=100:指定第几页,以及每页的记录数。 + - ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序 + - ?producy_type=1:指定筛选条件 + +- 异常响应 + + 当 RESTful API 接口出现非 2xx 的 HTTP 错误码响应时,采用全局的异常结构响应信息。 + + \ No newline at end of file diff --git "a/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/README.md" "b/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/README.md" new file mode 100644 index 0000000..4876f56 --- /dev/null +++ "b/\346\236\266\346\236\204\345\222\214\347\263\273\347\273\237\350\256\276\350\256\241/README.md" @@ -0,0 +1,124 @@ +## 架构和设计 + +架构和设计是面试中经常用到的,一是考察你的知识面,而是考察你接触的系统的复杂程序。本文可能介绍一些相关的问题。 + +### 1. 数据库结构设计 + +- 主从复制 +- 分库分表 +- 双主互备 +- 数据库中间件 + + + +为了解决单表数据过多,一般会采取分片【分库分表】水平切分,分片会产生路由,上层应用要知道数据在哪一个库中。 + +路由规则常用的有三种方法 + +- range 范围 + +- 哈希 +- 路由服务 + +#### 可用性 + +为了解决单点的故障,一般引入**主从复制**,也就是垂直分割。 + +采取**双主互备**【keplived】,冗余写库,双主当主从用 + +解决同步冲突,有两种常见解决方案 + +- 两个写库设置不同的初始化,相同步长增加id +- 业务层自己生成唯一的id,保证数据不冲突 + +#### 一致性 + +主从数据库的一致性,通常有两种解决方案: + +- 中间件 + +中间件屏蔽了集群了,对外伪装成一个server。 + +- 强制读主 + +### SESSION架构设计 + +服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。 只要用户不重启浏览器,会话就一直存在。 + +- session同步法:多台web-server相互同步数据 +- 客户端存储法:一个用户只存储自己的数据 +- 反向代理hash一致性:四层hash和七层hash都可以做,保证一个用户的请求落在一台web-server上 +- 后端统一存储:web-server重启和扩容,session也不会丢失 + +## 缓存架构设计 + +淘汰缓存机制 + +(1)淘汰缓存是一种通用的缓存处理方式 + +(2)先**淘汰缓存,再写数据库**的时序是毋庸置疑的 + +(3)服务化是向业务方屏蔽底层数据库与缓存复杂性的一种通用方式 + + + +设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应 + +### 缓存穿透 + +缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。 + +### 缓存穿透解决方案 + +有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 + +### 缓存雪崩 + +缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。 + +### 缓存雪崩解决方案 + +缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。 + +### 缓存击穿 + +对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。 + +缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 + +### 缓存击穿 解决方案 + +#### 1.使用互斥锁(mutex key) + +业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。 + +SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间 + +#### 2. "提前"使用互斥锁(mutex key): + +在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中 + +#### 3. "永远不过期": + +这里的“永远不过期”包含两层意思: + +> (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 +> +> (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期 + +​ 从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。 + +#### 4. 资源保护: + +采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。 + +四种解决方案:没有最佳只有最合适 + +| 解决方案 | 优点 | 缺点 | +| ----------------------------- | -------------------------------------------------------- | ------------------------------------------------------------ | +| 简单分布式互斥锁(mutex key) | 1. 思路简单 2. 保证一致性 | 1. 代码复杂度增大 2. 存在死锁的风险 3. 存在线程池阻塞的风险 | +| “提前”使用互斥锁 | 1. 保证一致性 | 同上 | +| 不过期(本文) | 1. 异步构建缓存,不会阻塞线程池 | 1. 不保证一致性。 2. 代码复杂度增大(每个value都要维护一个timekey)。 3. 占用一定的内存空间(每个value都要维护一个timekey)。 | +| 资源隔离组件hystrix(本文) | 1. hystrix技术成熟,有效保证后端。 2. hystrix监控强大。 | 1. 部分访问存在降级策略。 | + + \ No newline at end of file diff --git "a/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git.md" "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git.md" index de75849..0bb03b0 100644 --- "a/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git.md" +++ "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git.md" @@ -169,7 +169,7 @@ git config --global user.email johndoe@example.com 别名就是把一些复杂的命令简化 类似svn co等之类的 -``` +```shell git config --global alias.co checkout git config --global alias.ci commit git config --global alias.br branch @@ -182,7 +182,7 @@ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Crese 每个仓库的Git配置文件都放在.git/config文件中: -```shell +```ini [core] repositoryformatversion = 0 filemode = true @@ -202,7 +202,7 @@ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Crese 当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中 -``` +```ini [alias] co = checkout ci = commit @@ -211,4 +211,4 @@ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Crese [user] name = Your Name email = your@email.com -``` \ No newline at end of file +``` diff --git "a/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" new file mode 100644 index 0000000..a4abb5e --- /dev/null +++ "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" @@ -0,0 +1,100 @@ +在 Git 开发中通常会控制主干分支的质量,但有时还是会把错误的代码合入到远程主干。 虽然可以 直接回滚远程分支](https://harttle.land/2018/03/12/reset-origin-without-force-push.html), 但有时新的代码也已经合入,直接回滚后最近的提交都要重新操作。 那么有没有只移除某些 Commit 的方式呢?可以一次 [revert](https://git-scm.com/docs/git-revert) 操作来完成。 + +# 一个例子 + +考虑这个例子,我们提交了 6 个版本,其中 3-4 包含了错误的代码需要被回滚掉。 同时希望不影响到后续的 5-6。 + +``` +* 982d4f6 (HEAD -> master) version 6 +* 54cc9dc version 5 +* 551c408 version 4, harttle screwed it up again +* 7e345c9 version 3, harttle screwed it up +* f7742cd version 2 +* 6c4db3f version 1 +``` + +这种情况在团队协作的开发中会很常见:可能是流程或认为原因不小心合入了错误的代码, 也可能是合入一段时间后才发现存在问题。 总之已经存在后续提交,使得直接回滚不太现实。 + +下面的部分就开始介绍具体操作了,同时我们假设远程分支是受保护的(不允许 Force Push)。 思路是从产生一个新的 Commit 撤销之前的错误提交。 + +# git revert + +使用 `git revert ` 可以撤销指定的提交, 要撤销一串提交可以用 `..` 语法。 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。 + +``` +git revert --no-commit f7742cd..551c408 +git commit -a -m 'This reverts commit 7e345c9 and 551c408' +``` + +其中 `f7742cd` 是 version 2,`551c408` 是 version 4,这样被移除的是 version 3 和 version 4。 注意 revert 命令会对每个撤销的 commit 进行一次提交,`--no-commit` 后可以最后一起手动提交。 + +此时 Git 记录是这样的: + +``` +* 8fef80a (HEAD -> master) This reverts commit 7e345c9 and 551c408 +* 982d4f6 version 6 +* 54cc9dc version 5 +* 551c408 version 4, harttle screwed it up again +* 7e345c9 version 3, harttle screwed it up +* f7742cd version 2 +* 6c4db3f version 1 +``` + +现在的 HEAD(`8fef80a`)就是我们想要的版本,把它 Push 到远程即可。 + +# 确认 diff + +如果你像不确定是否符合预期,毕竟批量干掉了别人一堆 Commit,可以做一下 diff 来确认。 首先产生 version 4(`551c408`)与 version 6(`982d4f6`)的 diff,这些是我们想要保留的: + +``` +git diff 551c408..982d4f6 +``` + +然后再产生 version 2(`f7742cd`)与当前状态(HEAD)的 diff: + +``` +git diff f7742cd..HEAD +``` + +如果 version 3, version 4 都被 version 6 撤销的话,上述两个 diff 为空。 可以人工确认一下,或者 grep 掉 description 之后做一次 diff。 下面介绍的另一种方法可以容易地确认 diff。 + +# 另外一种方式 + +类似 [安全回滚远程分支](https://harttle.land/2018/03/12/reset-origin-without-force-push.html), 我们先回到 version 2,让它合并 version 4 同时代码不变, 再合并 version 5, version 6。 + +``` +# 从 version 2 切分支出来 +git checkout -b fixing f7742cd +# 合并 version 4,保持代码不变 +git merge -s ours 551c408 +# 合并 version 5, version 6 +git merge master +``` + +上述分支操作可以从 [分支管理](https://harttle.land/2016/09/02/git-workflow-branch.html) 一文了解。 至此,`fixing` 分支已经移除了 version 3 和 version 4 的代码,图示如下: + +``` +* 3cb9f8a (HEAD -> v2) Merge branch 'master' into v2 +|\ +| * 982d4f6 (master) version 6 +| * 54cc9dc version 5 +* | c669557 Merge commit '551c408' into v2 +|\ \ +| |/ +| * 551c408 version 4, harttle screwed it up again +| * 7e345c9 version 3, harttle screwed it up +|/ +* f7742cd version 2 +* 6c4db3f version 1 +``` + +可以简单 diff 一下来确认效果: + +``` +# 第一次 merge 结果与 version 2 的 diff,应为空 +git diff f7742cd..c669557 +# 第二次 merge 的内容,应包含 version 5 和 version 6 的改动 +git diff c669557..3cb9f8a +``` + +现在的 `HEAD`(即 `fixing` 分支)就是我们想要的版本,可以把它 Push 到远程了。 注意由于现在处于 `fixing` 分支, 需要 [Push 时指定远程分支](https://harttle.land/2016/09/05/git-workflow-remote.html) 为 `master`。 diff --git "a/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/gitcheat.jpg" "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/gitcheat.jpg" new file mode 100644 index 0000000..5dd105a Binary files /dev/null and "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/gitcheat.jpg" differ diff --git "a/\347\256\227\346\263\225/Readme.md" "b/\347\256\227\346\263\225/Readme.md" new file mode 100644 index 0000000..73cdf7e --- /dev/null +++ "b/\347\256\227\346\263\225/Readme.md" @@ -0,0 +1,243 @@ +## 算法 + +> 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。 + +### 算法的特征 + +- 有穷性 + +算法的有穷性是指算法必须能在执行有限个步骤之后终止; + +- 确定性 + +算法的每一步骤必须有确切的定义; + +- 输入 + +一个算法有0个或多个输入 + +- 输出 + +一个算法有一个或多个输出,以反映对输入数据加工后的结果 + +- 可行性 + +算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步 + +### 时间复杂度 + +时间复杂度是执行算法所需要的工作量,一般来说,计算机算法是问题规模n 的函数f(n),算法的时间复杂度也因此记做。 + +`T(n)=Ο(f(n))` + +因此,问题的规模n 越大,算法执行的时间的增长率与f(n) 的增长率正相关 + +### 空间复杂度 + +算法的空间复杂度是指算法需要消耗的内存空间。其计算和表示方法与时间 复杂度类似。 + +![时间复杂度](http://hi.csdn.net/attachment/201105/24/0_1306225542srVx.gif) + +### 常见算法 + +#### 排序算法 + +- [冒泡排序](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Sort/BubbleSort.php) + +```php +function BubbleSort(array $container) +{ + $count = count($container); + for ($j = 1; $j < $count; $j++) { + for ($i = 0; $i < $count - $j; $i++) { + if ($container[$i] > $container[$i + 1]) { + $temp = $container[$i]; + $container[$i] = $container[$i + 1]; + $container[$i + 1] = $temp; + } + } + } + return $container; +} +``` +- [插入排序](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Sort/InsertSort.php) + +```php +function InsertSort(array $container) +{ + $count = count($container); + for ($i = 1; $i < $count; $i++){ + $temp = $container[$i]; + $j = $i - 1; + // Init + while($j >= 0 && $container[$j] > $temp){ + $container[$j+1] = $container[$j]; + $j--; + } + if($i != $j+1) + $container[$j+1] = $temp; + } + return $container; +} +``` + +- [希尔排序](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Sort/ShellSort.php) + +```php +function ShellSort(array $container) +{ + $count = count($container); + for ($increment = intval($count / 2); $increment > 0; $increment = intval($increment / 2)) { + for ($i = $increment; $i < $count; $i++) { + $temp = $container[$i]; + for ($j = $i; $j >= $increment; $j -= $increment) { + if ($temp < $container[$j - $increment]) { + $container[$j] = $container[$j - $increment]; + } else { + break; + } + } + $container[$j] = $temp; + } + } + return $container; +} +``` + + + +- [选择排序](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Sort/SelectSort.php) + +```php +function SelectSort(array $container) +{ + $count = count($container); + for ($i = 0; $i < $count; $i++){ + $k = $i; + for ($j = $i + 1; $j < $count; $j++){ + if($container[$j] < $container[$k]){ + $k = $j; + } + } + if($k != $i){ + $temp = $container[$i]; + $container[$i] = $container[$k]; + $container[$k] = $temp; + } + } + return $container; +} +``` + +- [快速排序](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Sort/QuickSort.php) + +> 快速排序(Quicksort)是对冒泡排序的一种改进。他的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,整个排序过程可以递归进行,以达到整个序列有序的目的。 + +```php +function QuickSort(&$arr,$low,$high) +{ + if ($low < $high) { + $middle = getMiddle($arr,$low,$high); + QuickSort($arr,$low,$middle-1); + QuickSort($arr,$middle+1,$high); + } +} + +function getMiddle (&$arr,$low,$high) +{ + $temp = $arr[$low]; + while ($low < $high) { + while($low < $high && $temp <= $arr[$high]) { + $high --; + } + $arr[$low] = $arr[$high]; + while ($low < $high && $temp >= $arr[$low]) { + $low ++; + } + $arr[$high] = $arr[$low]; + } + $arr[$low] = $temp; + return $low; +} +``` + +- 归并排序 +- 堆排序 + +#### 查找算法 + +- 顺序查找 + +```php +function find($array ,$target) { + foreach ($array as $key=>$value) { + if($value === $target) { + return key; + } + } + return false; +} +``` + +- 有序查找(二分查找) + +```php +function BinaryQueryRecursive(array $container, $search, $low = 0, $top = 'default') +{ + $top == 'default' && $top = count($container); + if ($low <= $top) { + $mid = intval(floor($low + $top) / 2); + if (!isset($container[$mid])) { + return false; + } + if ($container[$mid] == $search) { + return $mid; + } + if ($container[$mid] < $search) { + return BinaryQueryRecursive($container, $search, $mid + 1, $top); + } else { + return BinaryQueryRecursive($container, $search, $low, $mid - 1); + } + } +} +``` + +- 动态查找(BST) +- 哈希表 O(1) + +### 算法的思想 + +- 迭代 +- 递归 +- 动态规划 +- 回溯 +- 分治 +- 贪心 + +### 算法相关的面试题 + +- 字符串 + + - 查找字符串中的字符 + - 翻转字符串 + +- 排序 + + - 冒泡排序 + - 快速排序 + - 归并排序 + +- 链表 + + - 翻转链表 + - 链表有没有环 + +- 二叉搜索树 + + - 二叉树的深度 + - 二叉树的遍历 + - 重建二叉树 + + + + diff --git "a/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" new file mode 100644 index 0000000..251be5d --- /dev/null +++ "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" @@ -0,0 +1,156 @@ +## :算法之排序二分查找 +### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +### :pencil2:二分查找 + + +​ + + + +**二分查找是一种常见的查找方式。在生活中有一个猜大小的例子。比如给定一个0-100的范围,让你猜出事先我所设置的数字,每次把数字的范围缩小到一半。第一次猜50,大了还是小了,继续缩小数字的范围。但是有一个前提,我们得保证我们查找的是一个有序的范围。** + +**** + +### :pencil2:查找思想 + +**二分查找针对的是一个有序的集合。它的查找算法有点类型分治的思想,就像上面所说的。每次我通过中间的数和指定的数进行比较,判断大小,缩小猜测的区间,最多到区间等于0的时候,结果也就是最终指定的答案。二分查找是一种高效的查找算法,它的时间复杂度是O(logn).** + +**** + +### :pencil2:php实现二分查找 + +**1.我们先实现一个最基础的,在一个有序数组中(数组没有重复的值)查找给定值。(迭代)** + +```php +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + // $middle=floor(($l+$r)/2); + // $middle=$l+floor(($r-$l)/2); + $middle=$l+(($r-$l)>>1); //使用位运算查找更高效 + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$res) $r=$middle-1; + else $l=$middle+1; + } + return -1; +} +$data=[2,5,6,7,12,34]; +$res=12; +var_dump(binarySerach($data,$res)); +``` +**** + +**使用递归实现刚才的操作。** + +```php +function binarySerach($data,$res){ + return recursion($data,0,count($data)-1,$res); +} + +function recursion($data,$l,$r,$res) +{ + if($l>$r){ + return -1; + } + $middle=$l+(($r-$l)>>1); + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$res) return recursion($data,$l,$middle-1,$res); + else return recursion($data,$middle+1,$r,$res); +} +$data=[2,5,6,7,12,34]; +$res=12; +var_dump(binarySerach($data,$res)); +``` +**** + + +### :pencil2:二分查找的变形问题 + +**上面的那个注释是在数字没有重复的情况下,现在我们来实现数组中有重复值的情况下,如何查找出第一个等于给定值的元素。其实就是在做等于判断的时候如果索引是0那么是第一个等于给定值的数,或者当前等于给定值的上一个索引值不等于给定值。** + +```php + +function binarySerach($data,$res){ + $l=0; + $r=count($data); + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]>$res) $r=$middle-1; + elseif($data[$middle]<$res) $l=$middle+1; + else{ + if($middle==0 || $data[$middle-1] !==$res) return $middle; + else $r=$middle-1; + } + } +} +$data=[2,5,6,7,8,8,10]; +$res=8; +var_dump(binarySerach($data,$res)); +``` +**** +**查找第一个大于等于给定值的元素。** +```php + +//查找第一个大于等于给定值的数 +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]<$res) $l=$middle+1; + else{ + if($middle==0 || $data[$middle-1]<$res ) return $middle; + else $r=$middle-1; + } + } + +} +$data=[2,5,6,7,8,8,10]; +$res=9; +var_dump(binarySerach($data,$res)); +``` +**** + +**针对数组是一个循环有序的数组Leetcode35题** +```php + +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$data[$r]){ + if($data[$l]<=$res && $data[$middle]>$res) $r=$middle-1; + else $l=$middle+1; + }else{ + if($data[$middle]<=$res && $data[$r] >$res) $r=$middle-1; + else $l=$middle+1; + } + } +} +$data=[5,6,7,8,1,2,3,4]; +$res=7; +var_dump(binarySerach($data,$res)); +``` + +**当然二分查找还有很多的应用场景,我就总结到这了。** + + + + +### 联系 + + +​ + + + + + + diff --git "a/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" new file mode 100644 index 0000000..4443d82 --- /dev/null +++ "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" @@ -0,0 +1,263 @@ +## :算法算法之动态规划 + +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +***动态规划需要花很长时间去练习巩固的。其实在理解动态规划之前正确的顺序应该是先写递归,因为很多动态规划的转移方程都是通过递归加记忆法从而推导出公式,但是我随性,所以就不按套路出牌了。接下来就是简单介绍一下动态规划以及应用场景,最后把之前刷过的动态规划的题目全放出来,方便一次性查看。*** +**** + +### :pencil2:动态规划的介绍 + +***动态规划背后的思想简单概括就是,若要解一个指定的问题,我们需要解它的不同部分问题(子问题),再合并子问题求出最终想要的解。*** + +***在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖于当前面临的状态,又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线。这种把一个问题看做是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策最优化问题。每个阶段中,都求出本阶段的各个初始状态到过程终点的最短路径和最短距离,当逆序倒推到过程起点时,便得到了全过程的最短路径及最短距离,同时附带得到了一组最优结果。*** + + +**** + +### :pencil2:动态规划的步骤 + + ***1.递归+记忆化 ->反向推出递推公式*** + + ***2.状态的定义 opt[n],dp[n].*** + + ***3.状态转移的方程dp[n]=dp[n-1]+dp[n-2]*** + + ***4.最优子结构*** +****** +**最经典的爬楼梯问题,给定一个数字代表着楼梯的层数,每次你可以走一步或者两步,求最终你可以有几种方式到达顶峰。递归是自上而下,一层层调用,到了递归的出口,又一层层的回来。而动态规划是从下而上的去思考,比如还是这个爬楼梯问题,如果是动态规划的思想就是** +```php + +f[n]=f[n-1]+f[n+2]; +//一个斐波那契数列 +``` + +***动态规划需要大量的实战练习,一般你只要记住两步,定义好状态,有些时候单单定义一个一维数组的状态是不够的,二通过状态的定义推出递推公式。具体看场景使用。(下面是leetcode70,72,120,121,122,123,152,300动态规划的题目)*** + + +​ + + +```php + /** + * @param Integer $n + * @return Integer + */ + function climbStairs($n) { + if($n<=1){ + return 1; + } + $res[0]=1; + $res[1]=1; + for($i=2;$i<=$n;$i++){ + $res[$i]=$res[$i-1]+$res[$i-2]; + } + return $res[$n]; + + } +``` +****** + + + +​ + + +```php + /** + * @param String $word1 + * @param String $word2 + * @return Integer + */ + function minDistance($word1, $word2) { + + for($i=0;$i<=strlen($word1);$i++) $dp[$i][0]=$i; + for($i=0;$i<=strlen($word2);$i++) $dp[0][$i]=$i; + + for($i=1;$i<=strlen($word1);$i++){ + for($j=1;$j<=strlen($word2);$j++){ + if(substr($word1,$i-1,1)==substr($word2,$j-1,1)){ + $dp[$i][$j]=$dp[$i-1][$j-1]; + }else{ + $dp[$i][$j]=min($dp[$i-1][$j],$dp[$i][$j-1],$dp[$i-1][$j-1])+1; + } + } + } + return $dp[strlen($word1)][strlen($word2)]; + } +``` +****** + + + +​ + + +```php + /** + * @param Integer[][] $triangle + * @return Integer + */ + function minimumTotal($triangle) { + if(empty(count($triangle))){ + return 0; + } + for($i=count($triangle)-1;$i>=0;$i--){ + for($j=0;$j +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + + //二维数组的0,1,2表示的状态分别是没买股票,买了股票, 卖了股票 + $res=0; + $pro[0][0]=$pro[0][2]=0; + $pro[0][1]=-$prices[0]; + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + $dp[0][0]=0; + $dp[0][1]= -$prices[0]; + $res=0; + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + $res=0; + $dp[0][0][0]=0; + $dp[0][0][1]= -$prices[0]; + $dp[0][1][0]= -$prices[0]; + $dp[0][1][1]= -$prices[0]; + $dp[0][2][0]=0; + + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $nums + * @return Integer + */ + function maxProduct($nums) { + $max=$min=$res=$nums[0]; + for($i=1;$i +​ + + +```php + +/** + * @param Integer[] $nums + * @return Integer + */ + function lengthOfLIS($nums) { + if(empty($nums)){ + return 0; + } + $res=1; + for($i=0;$i +​ + + + + + + diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" index e8ae82a..2f329c8 100644 --- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" @@ -39,7 +39,7 @@ HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较 3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 4. HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。 -![img](http://tenny.qiniudn.com/HTTPQUBIE2.png) +![img](images/https.png) ## 使用SPDY加快你的网站速度 @@ -64,5 +64,23 @@ SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTT - **header压缩,**如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。 - **服务端推送**(server push),同SPDY一样,HTTP2.0也具有server push功能。目前,有大多数网站已经启用HTTP2.0,例如[YouTuBe](https://www.youtube.com/),[淘宝网](http://www.taobao.com/)等网站,利用chrome控制台可以查看是否启用H2 -![img](http://tenny.qiniudn.com/diff332.png) +![img](images/http2.png) +```conf +server { + listen 443 ssl http2; + server_name example.com; + root /var/www/example.com/public; + + # SSL + ssl_certificate /etc/live/example.com/fullchain.pem; + ssl_certificate_key /etc/live/example.com/privkey.pem; + ssl_trusted_certificate /etc/live/example.com/chain.pem; + + # index.html fallback + location / { + try_files $uri $uri/ /index.html; + } +} + +``` \ No newline at end of file diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/IP\345\215\217\350\256\256.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/IP\345\215\217\350\256\256.md" new file mode 100644 index 0000000..a7d7b54 --- /dev/null +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/IP\345\215\217\350\256\256.md" @@ -0,0 +1,39 @@ +## IP协议 + +> IP协议是TCP/IP协议簇中的核心协议,也是TCP/IP的载体。所有的TCP,UDP,ICMP及IGMP数据都以IP数据报格式传输。 IP提供**不可靠**的,**无连接**的数据传送服务。 不可靠指它不能保证IP数据报能成功到达目的地 + +不可靠(unreliable)的意思是它不能保证IP数据报能成功地到达目的地。IP仅提供最好的传输服务。如果发生某种错误时,如某个路由器暂时用完了缓冲区,IP有一个简单的错误处理算法:丢弃该数据报,然后发送ICMP消息报给信源端。任何要求的可靠性必须由上层来提供(如TCP)。 + +无连接(connectionless)这个术语的意思是IP并不维护任何关于后续数据报的状态信息。每个数据报的处理是相互独立的。这也说明,IP数据报可以不按发送顺序接收。如果一信源向相同的信宿发送两个连续的数据报(先是A,然后是B),每个数据报都是独立地进行路由选择,可能选择不同的路线,因此B可能在A到达之前先到达。 + +![img](http://blog.chinaunix.net/attachment/201304/27/26833883_1367053079KNJe.png) + + + +## IP报文介绍 + +![(../MQ/images/52im_1.png)](http://docs.52im.net/extend/docs/book/tcpip/vol1/3/images2/52im_1.png) + +- 4位版本号。包含IP数据报的版本号:ipv4为4,ipv6为6 +- 4位首部长度 +- 8位服务类型 +- 16位总长度 +- 标识字段:长度为16位,最多分配的ID值为65535个 + +​ + + + +### IP地址分类 + +IPV4被分为五大类:ABCDE + +A类为:点分四组中的第一组地址范围为0~127的IP地址。已二进制来看就是“首位为0” + +B类:128~191.二进制首位为10 + +C类:192~223.二进制首位为110 + +D类:224~239.二进制首位为1110 + +E类:240~255.二进制首位为1111 \ No newline at end of file diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\256.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\256.md" index 6b3cb1a..8d6b12a 100644 --- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\256.md" +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\256.md" @@ -191,3 +191,16 @@ ![Alt text](https://raw.githubusercontent.com/zqjflash/tcp-ip-protocal/master/tcp-four-disconnect-4-2.png) +## TCP粘包 + +> 世界上本没有“粘包”,只不过是少数人没有正确处理TCP数据边界问题,成熟的应用层协议(http、ssh)都不会存在这个问题。但是如果你使用纯TCP自定义协议,那就需要自己处理好了。 +TCP是面向字节流的传输,一个数据包可能会被拆成多个小的数据包进行发送,这样就会和其他的数据包连在一起,形成粘包。 +举个例子,比如发送两个"hello world" 但是可能服务端收到的是"hello" 和 "worldhello world"。第一次的数据包和第二次的数据连在一起了。 + +"粘包"的原因 + - 当连续发送数据时,由于tcp协议的nagle算法,会将较小的内容拼接成大的内容,一次性发送到服务器端,因此造成粘包 + - 当发送内容较大时,由于服务器端的recv(buffer_size)方法中的buffer_size较小,不能一次性完全接收全部内容,因此在下一次请求到达时,接收的内容依然是上一次没有完全接收完的内容,因此造成粘包现象 +解决粘包的方案 +- 使用特殊的字符或字符串作为消息的边界,例如 HTTP 协议的 headers 以“\r\n”为字段的分隔符,但是如果消息中不能含有定界符,否则会导致消息混乱 +- 消息长度固定,消息长度不足,空白填充 提前确定包长度,读取的时候也安固定长度读取,适合定长消息包。 +- 自定义协议,将消息分为消息头和消息体,消息头中包含表示消息总长度。 diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" new file mode 100644 index 0000000..37e0ab4 --- /dev/null +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" @@ -0,0 +1,194 @@ +## Websocket协议 + +> Websocket是html5提出的一个协议规范,参考rfc6455。 +> +> websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便C-S之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。 +> +> WebSocket是为解决客户端与服务端实时通信而产生的技术。websocket协议本质上是一个基于tcp的协议,是先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。 + + + +### Websocket和HTTP协议的关系 + +同样作为应用层的协议,WebSocket在现代的软件开发中被越来越多的实践,和HTTP有很多相似的地方,这里将它们简单比较: + +#### 相同点 + +1. 都是基于TCP的应用层协议。 +2. 都使用Request/Response模型进行连接的建立。 +3. 在连接的建立过程中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码。 +4. 都可以在网络中传输数据。 + +#### 不同点 + +1. WS使用HTTP来建立连接,但是定义了一系列新的header域,这些域在HTTP中并不会使用。 +2. WS的连接不能通过中间人来转发,它必须是一个直接连接。 +3. WS连接建立之后,通信双方都可以在任何时刻向另一方发送数据。 +4. WS连接建立之后,数据的传输使用帧来传递,不再需要Request消息。 +5. WS的数据帧有序。 + + + +### 协议 + +#### 1. 握手 + +客户端发起握手。 + +```http +GET /chat HTTP/1.1 +Host: server.example.com +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Origin: http://example.com +Sec-WebSocket-Protocol: chat, superchat +Sec-WebSocket-Version: 13 +``` + +从上面的协议中可以看到websocket基于HTTP协议的GET。而且只支持GET. + +**Upgrade**:upgrade是HTTP1.1中用于定义转换协议的header域。它表示,如果服务器支持的话,客户端希望使用现有的「网络层」已经建立好的这个「连接(此处是TCP连接)」,切换到另外一个「应用层」(此处是WebSocket)协议。 + +**Connection**:HTTP1.1中规定Upgrade只能应用在「直接连接」中,所以带有Upgrade头的HTTP1.1消息必须含有Connection头,因为Connection头的意义就是,任何接收到此消息的人(往往是代理服务器)都要在转发此消息之前处理掉Connection中指定的域(不转发Upgrade域)。 + +**Sec-WebSocket-***:第7行标识了客户端支持的子协议的列表(关于子协议会在下面介绍),第8行标识了客户端支持的WS协议的版本列表,第5行用来发送给服务器使用(服务器会使用此字段组装成另一个key值放在握手返回信息里发送客户端)。 + +服务端响应 + +```http +HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Sec-Websocket-Accept: ZEs+c+VBk8Aj01+wJGN7Y15796g= +Connection: Upgrade +Sec-WebSocket-Protocol: chat, superchat +``` + +Sec-Websocket-Accept 是一个校验。用客户端发来的sec_key 服务器通过sha1计算拼接商GUID【258EAFA5-E914-47DA-95CA-C5AB0DC85B11 】 。然后再base64encode + +#### 数据传输 + +客户端和服务器连接成功后,就可以进行通信了,通信协议格式是WebSocket格式,服务器端采用Tcp Socket方式接收数据,进行解析,协议格式如下: + +![img](https://images2015.cnblogs.com/blog/827837/201604/827837-20160426152052033-1112357880.jpg) + + 这里使用的是数据存储的位(bit),当进行加密的时候,最终要的一位就是最左边的第一个。 + +- FIN :1bit ,表示是消息的最后一帧,如果消息只有一帧那么第一帧也就是最后一帧。 + +- RSV1,RSV2,RSV3:每个1bit,必须是0,除非扩展定义为非零。如果接受到的是非零值但是扩展没有定义,则需要关闭连接。 + +- Opcode:4bit,解释Payload数据,规定有以下不同的状态,如果是未知的,接收方必须马上关闭连接。状态如下:0x0(附加数据帧) 0x1(文本数据帧) 0x2(二进制数据帧) 0x3-7(保留为之后非控制帧使用) 0xB-F(保留为后面的控制帧使用) 0x8(关闭连接帧) 0x9(ping) 0xA(pong) + +- + + Mask:1bit,掩码,定义payload数据是否进行了掩码处理,如果是1表示进行了掩码处理。 + + ``` + Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。 + ``` + +- Payload length:7位,7 + 16位,7+64位,payload数据的长度,如果是0-125,就是真实的payload长度,如果是126,那么接着后面的2个字节对应的16位无符号整数就是payload数据长度;如果是127,那么接着后面的8个字节对应的64位无符号整数就是payload数据的长度。 + +- Masking-key:0到4字节,如果MASK位设为1则有4个字节的掩码解密密钥,否则就没有。 + +- Payload data:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。 + +### 服务端简单实现 +```php +// 封装ws 协议的数据包 +function build($msg) { + $frame = []; + $frame[0] = '81'; // 81 就是 10000001 第一位1表示最后一个数据段,最后一位1表示这是文本数据 + $len = strlen($msg); + if ($len < 126) { + //7位长度 第一个是掩码 默认是0 + //小于126的时候 也是 01111110 数据包第二个字节表示长度 + $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); + } else if ($len < 65025) { + //7位 + 16位 01111110 00000000 00000000 + $s = dechex($len); + $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s; + } else { + //7位 + 64位 01111111 00000000 00000000 + $s = dechex($len); + $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s; + } + $data = ''; + $l = strlen($msg); + for ($i = 0; $i < $l; $i++) { + $data .= dechex(ord($msg{$i})); + } + //最后是数据内容 + $frame[2] = $data; + $data = implode('', $frame); + return pack("H*", $data); +} +//拆包 +function parse($buffer) { + $decoded = ''; + $len = ord($buffer[1]) & 127; + if ($len === 126) { + $masks = substr($buffer, 4, 4); + $data = substr($buffer, 8); + } else if ($len === 127) { + $masks = substr($buffer, 10, 4); + $data = substr($buffer, 14); + } else { + $masks = substr($buffer, 2, 4); + $data = substr($buffer, 6); + } + for ($index = 0; $index < strlen($data); $index++) { + $decoded .= $data[$index] ^ $masks[$index % 4]; + } + return $decoded; + } +$socket = stream_socket_server("tcp://0.0.0.0:8888", $errno, $errstr); +if (!$socket) { + echo "$errstr ($errno)
\n"; + die; +} +while (1) { + $conn = stream_socket_accept($socket); + $data = stream_get_contents($conn,500); + $data = explode("\r\n",$data); + $secKey = ""; + foreach ($data as $key=>$val) { + if (strpos($val,"WebSocket-Key:") >= 1 ) { + $key = explode(":",$val ); + $secKey = $key[1]; + } + } + //固定key 算法 base64(sha1(key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11)) + $hashkey = base64_encode(sha1(trim($secKey)."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); + $ws = "HTTP/1.1 101 Switching Protocols\r\n"; + $ws .= "Upgrade: websocket\r\n"; + $ws .= "Connection: Upgrade\r\n"; + $ws .= "Sec-WebSocket-Version:13\r\n"; + $ws .= "Sec-WebSocket-Accept: $hashkey\r\n"; + $ws .= "Sec-WebSocket-Protocol: chat\r\n"; + $ws .= "\r\n"; + if (!$conn) { + continue; + } + fwrite($conn, $ws); + fwrite($conn,build("hello")); + fclose($conn); + break; +} +fclose($socket); +``` + +### 客户端 + +```js +var websocket = new WebSocket("ws://127.0.0.1:8888","chat") +websocket.onopen = function(){ + +} + +websocket.onmessage = function(){} + +websocket.onclose = function(){} +``` + diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" new file mode 100644 index 0000000..9cf1a93 Binary files /dev/null and "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" differ diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" new file mode 100644 index 0000000..c998d85 Binary files /dev/null and "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" differ diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/Behavioral.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/Behavioral.md" new file mode 100644 index 0000000..bff6b3f --- /dev/null +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217/Behavioral.md" @@ -0,0 +1,214 @@ +## 行为型设计模式 + +### 1. 观察者模式 + +> Behavioral定义对象间一种一对多的依赖关系,使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 + +Subject 被观察者。是一个接口或者是抽象类,定义被观察者必须实现的职责,它必须能偶动态地增加、取消观察者,管理观察者并通知观察者。 + +Observer 观察者。观察者接收到消息后,即进行 update 更新操作,对接收到的信息进行处理。 + +ConcreteSubject 具体的被观察者。定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。 + +ConcreteObserver 具体观察者。每个观察者在接收到信息后处理的方式不同,各个观察者有自己的处理逻辑。 + + + +观察者模式简单的理解,就是当一个类改变的时候,通知另外一个类进行做出处理。比如我们用户在注册成功的时候,会发送短信提醒。我们可以利用观察者模式,实现应用的解耦。 + +```php +//抽象主题类 +interface Subject +{ + public function attach(Observer $Observer); + + public function detach(Observer $observer); + + //通知所有注册过的观察者对象 + public function notifyObservers(); +} + +//具体主题角色 + + +class ConcreteSubject implements Subject +{ + private $_observers; + + public function __construct() + { + $this->_observers = array(); + } + + //增加一个观察者对象 + public function attach(Observer $observer) + { + return array_push($this->_observers,$observer); + } + + //删除一个已经注册过的观察者对象 + + public function detach(Observer $observer) + { + $index = array_search($observer,$this->_observers); + if($index === false || !array_key_exists($index, $this->_observers)) + return false; + unset($this->_observers[$index]); + return true; + } + + //通知所有注册过的观察者 + public function notifyObservers() + { + if(!is_array($this->_observers)) return false; + foreach($this->_observers as $observer) + { + $observer->update(); + } + return true; + } +} + + +//抽象观察者角色 + +interface Observer +{ + //更新方法 + public function update(); +} + +//观察者实现 +class ConcreteObserver implements Observer +{ + private $_name; + + public function __construct($name) + { + $this->_name = $name; + } + + //更新方法 + + public function update() + { + echo 'Observer'.$this->_name.' has notify'; + } +} + +$Subject = new ConcreteSubject(); + +//添加第一个观察者 + +$observer1 = new ConcreteObserver('baixiaoshi'); +$Subject->attach($observer1); +echo 'the first notify:'; +$Subject->notifyObservers(); + +//添加第二个观察者 +$observer2 = new ConcreteObserver('hurong'); +echo '
second notify:'; +$Subject->attach($observer2); + +``` + +- 利用SPLObserver 和SplSubject + +```php +SplSubject { + /* 方法 */ + abstract public void attach ( SplObserver $observer ) + abstract public void detach ( SplObserver $observer ) + abstract public void notify ( void ) +} +SplObserver { + /* 方法 */ + abstract public void update ( SplSubject $subject ) +} +``` + +```php +/** +* Subject,that who makes news +*/ +class Newspaper implements \SplSubject{ + private $name; + private $observers = array(); + private $content; + + public function __construct($name) { + $this->name = $name; + } + + //add observer + public function attach(\SplObserver $observer) { + $this->observers[] = $observer; + } + + //remove observer + public function detach(\SplObserver $observer) { + + $key = array_search($observer,$this->observers, true); + if($key){ + unset($this->observers[$key]); + } + } + + //set breakouts news + public function breakOutNews($content) { + $this->content = $content; + $this->notify(); + } + + public function getContent() { + return $this->content." ({$this->name})"; + } + + //notify observers(or some of them) + public function notify() { + foreach ($this->observers as $value) { + $value->update($this); + } + } +} + +/** +* Observer,that who recieves news +*/ +class Reader implements SplObserver{ + private $name; + + public function __construct($name) { + $this->name = $name; + } + + public function update(\SplSubject $subject) { + echo $this->name.' is reading breakout news '.$subject->getContent().'
'; + } +} + +$newspaper = new Newspaper('Newyork Times'); + +$allen = new Reader('Allen'); +$jim = new Reader('Jim'); +$linda = new Reader('Linda'); + +//add reader +$newspaper->attach($allen); +$newspaper->attach($jim); +$newspaper->attach($linda); + +//remove reader +$newspaper->detach($linda); + +//set break outs +$newspaper->breakOutNews('USA break down!'); +``` + + + +## 参考资料 + +- [http://php.net/manual/zh/class.splobserver.php#112587](http://php.net/manual/zh/class.splobserver.php#112587) +- [http://designpatternsphp.readthedocs.io/en/latest/Behavioral/Observer/README.html](http://designpatternsphp.readthedocs.io/en/latest/Behavioral/Observer/README.html) + diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/Creational.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/Creational.md" new file mode 100644 index 0000000..b03f185 --- /dev/null +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217/Creational.md" @@ -0,0 +1,69 @@ +## 工厂模式 + +### 1. 简单工厂 + +简单工厂就是一个工厂,只创建一个单一的类。不能创建其他的类,这就是简单工厂 + +```php +class Factory { + + public function create() + { + return new A(); + } +} + +``` + +这个简单的工厂只能创建A类。 + +### 2. 工厂方法 + +- [模式动机](http://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/factory_method.html#id16) + +关于工厂模式的定义动机可以查阅[工厂模式](http://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/factory_method.html#id17)。 + +我只写下自己的理解 + +有些时候,我们需要一个工厂创建不同的产品。比如军工厂既能创建子弹,又能创建枪。 + +工厂方法模式包含如下角色: + +- Product:抽象产品 +- ConcreteProduct:具体产品 +- Factory:抽象工厂 +- ConcreteFactory:具体工厂 + +![../_images/FactoryMethod.jpg](http://design-patterns.readthedocs.io/zh_CN/latest/_images/FactoryMethod.jpg) + + + +```php +interface Ifactory{ + public function create($type); +} + +class Factory implements Ifactory{ + public function create($type){ + if($type == 'A') { + return new A(); + } else if($type == 'B') { + return new B(); + } + } +} +``` + +### 3. 抽象工厂 + +[抽象工厂详细介绍](http://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/abstract_factory.html) + +简单的个人理解:抽象工厂是为了创建不同的类型的产品。抽象出工厂。让不同的工厂创建不同的产品 + +举个例子。比如抽象军工厂。军工厂A生产枪,军工厂B生产子弹。 + +![../_images/AbatractFactory.jpg](http://design-patterns.readthedocs.io/zh_CN/latest/_images/AbatractFactory.jpg) + +代码可以参考 + +[https://github.com/domnikl/DesignPatternsPHP/blob/master/Creational/AbstractFactory](https://github.com/domnikl/DesignPatternsPHP/blob/master/Creational/AbstractFactory) \ No newline at end of file diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/README.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/README.md" new file mode 100644 index 0000000..b71cf44 --- /dev/null +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217/README.md" @@ -0,0 +1,53 @@ +## 设计模式 + +> 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 + +设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因 + +### 设计模式的类型 + +根据设计模式的参考书 **Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)** 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns) + +| 序号 | 模式 & 描述 | 包括 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | **创建型模式** 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern) | +| 2 | **结构型模式** 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式(Flyweight Pattern)代理模式(Proxy Pattern) | +| 3 | **行为型模式** 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern)命令模式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式(Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式(State Pattern)空对象模式(Null Object Pattern)策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern) | + +### 设计模式的六大原则 + +**1、开闭原则(Open Close Principle)** + +开闭原则的意思是:**对扩展开放,对修改关闭**。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类, + +> 实现热插拔,提高扩展性。 + +**2、里氏代换原则(Liskov Substitution Principle)** + +里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 + +> 实现抽象的规范,实现子父类互相替换; + +**3、依赖倒转原则(Dependence Inversion Principle)** + +这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 + +> 针对接口编程,实现开闭原则的基础; + +**4、接口隔离原则(Interface Segregation Principle)** + +这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 + +> 降低耦合度,接口单独设计,互相隔离; + +**5、迪米特法则,又称最少知道原则(Demeter Principle)** + +最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 + +> 功能模块尽量独立 + +**6、合成复用原则(Composite Reuse Principle)** + +合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。 + +> 尽量使用聚合,组合,而不是继承; \ No newline at end of file diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/Structural.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/Structural.md" new file mode 100644 index 0000000..72aa8db --- /dev/null +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217/Structural.md" @@ -0,0 +1,158 @@ +## 结构型 + +结构型模式(Structural Pattern)描述如何将类或者对 象结合在一起形成更大的结构,就像搭积木,可以通过 简单积木的组合形成复杂的、功能更为强大的结构。 + +### 1. 适配器模式 + +适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 + +适配器就是一种适配中间件,它存在于不匹配的二者之间,用于连接二者,将不匹配变得匹配,简单点理解就是平常所见的转接头,转换器之类的存在。 + +适配器模式包含如下角色: + +- Target:目标抽象类 +- Adapter:适配器类 +- Adaptee:适配者类 +- Client:客户类 + +![../_images/Adapter_classModel.jpg](http://design-patterns.readthedocs.io/zh_CN/latest/_images/Adapter_classModel.jpg) + +#### 类适配器 + +一句话描述:Adapter类,通过**继承** src类,**实现** dst 类**接口**,完成src->dst的适配。 + +用电源适配器举例,我们需要5v的直流电,但是现在有的是220v的交流电。需要通过适配器转换 + +![这里写图片描述](../MQ/images/20161018130024488) + +```php +class V220{ + public function output220V() + { + return "220"; + } +} +interface V5{ + public function output5V() + { + + } +} + +class Adapter extends V220 implements V5{ + //将220v转化成5v + public function output5V() + { + $v = $this->output220V(); + return $v/44; + } +} + +``` + +这里可能有人会问,直接写一个类,然后 返回对应的5v不就行了吗。但是我们适配器模式的是将不合适的类,通过一个接口转成合适的。你直接写一个类的话,就是增加了一个类。而不是利用原来的类的功能。 + +#### 对象适配器 + +基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承src类,而是持有src类的实例,以解决**兼容性**的问题。  即:**持有** src类,**实现** dst 类**接口**,完成src->dst的适配。  (根据“合成复用原则”,在系统中**尽量使用关联关系来替代继承关系**,因此大部分结构型模式都是对象结构型模式。) + + ```php +class Adapter implements V5{ + public $v; + public function __construct(V220 $v200) + { + $this->v = $v200 + } + public function output5V() + { + if(!$this->v) { + $v = $this->v->output220V(); + return $v/44; + } + + return 0; + } +} + ``` + +![这里写图片描述](../MQ/images/20161018144117548) + +### 2. 装饰器模式 + +> 装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。 + +装饰模式包含如下角色: + +- Component: 抽象构件 +- ConcreteComponent: 具体构件 +- Decorator: 抽象装饰类 +- ConcreteDecorator: 具体装饰类 + +![../_images/Decorator.jpg](../MQ/images/Decorator.jpg) + + + +```php +interface Component{ + public function run(){} +} + +class Car implements Component{ + public function run(){ + echo " I am run"; + } +} + +class Decorator { + public function __construct(Car $car) + { + $this->car = $car; + } + public function run() + { + $this->car->run(); + $this->fly(); + } + protected function fly() + { + echo "fly"; + } +} + +$car = new Car(); + +$decorator = new Decorator(); + +``` + + + +### 3. 门面模式 + +部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式 + +![](../MQ/images/20160516173232943) +![](../MQ/images/20160516173308130) + + + + + + +在这个对象图中,出现了两个角色: + +门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。 + +子系统(subsystem)角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。 + +简单的理解。就是门面模式就是解决客户端调用子系统的复杂度,为客户端提供统一的调用 + + + + + + + +### 阅读资料 + +- [适配器模式](https://blog.csdn.net/zxt0601/article/details/52848004) \ No newline at end of file diff --git "a/\351\235\242\350\257\225/04\351\235\242\350\257\225\346\217\220\351\227\256.md" "b/\351\235\242\350\257\225/04\351\235\242\350\257\225\346\217\220\351\227\256.md" new file mode 100644 index 0000000..7255139 --- /dev/null +++ "b/\351\235\242\350\257\225/04\351\235\242\350\257\225\346\217\220\351\227\256.md" @@ -0,0 +1,27 @@ +## 面试提问 + +面试到最后的时候,通常会让你提问。这个时候,你需要准备好几个问题。表示你有准备的。不要最后一面,不要问薪资。 + +1. 贵公司这个职位是新增还是接手离职员工的任务 + +了解公司招聘人的目的。 + +2. 贵公司一般的团队是多大,几个人负责一个产品或者业务? + +了解公司的实力,这个一般在官网都有介绍,但是你听下里面的人介绍下,更了解里面的情况 + +3. 贵公司的开发中是否会使用到一些最新技术 + +了解公司的技术栈,更好的对自己的职业发展。 + +4. 贵公司之前遇到了什么样的技术难题,怎么解决的? + +这个可以当作学习的例子。对方可能之前问过你之前的项目的技术难题,这个你可以反问,来学习对方的技术难题,以及方案。 + +5. 你觉得我有哪些需要提高的地方 ?可以给我点建议吗? + +需要对方给自己简单的评价,完善自己的不足。 + +6. 您觉得这个职位未来几年的职业发展是怎样的 + +了解该职位是不是和未来的职业规划相符。间件的询问有没有晋升的机会。 \ No newline at end of file diff --git "a/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" "b/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" new file mode 100644 index 0000000..e6964a4 --- /dev/null +++ "b/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" @@ -0,0 +1,27 @@ +谈薪资的时候,基本上是我们到了面试的最后的步骤了,这个时候,你的报价决定你以后的工作的工资的多少。也是很关键的一步,我之前面试的就是由于缺乏经验,导致自己报价低了。然后入职之后再往上加,就很难了。 + + + +在我们面试的时候,我们**尽量不要先出牌**,比如我们在填表格的时候,期望薪资, 可以写上面谈,或者写一个范围区间。不要写一个具体的数字,因为很多hr人员都会压低你的工资。比如你写的是20k,可能hr会给你18k。 + +- 你期望的薪资是多少? + +**了解该公司所在地区、所属行业、公司规模等信息**,你的薪水要求应该在该公司所在地 区、行业、公司规模相应的薪水范围之内。**尽可能提供一个你期望的薪水范围,而不是具体的薪金数。** 了解具体的福利情况。好的福利可以让你的薪资加上不少。比如有的公司是15薪,然后公司稍低点,有的工资高,但是福利少。你可以权衡一下。 + + + +- 你的上一份的薪资是多少? + + 问你这个问题的时候,是想知道和你当前薪资对比,比如你上一份是10k ,这次你报价20k,别人应该只会给你15k。所以上一份的薪资,应该和本次的报价,不是特别差别大。但是也不能一样。不然你跳槽就没不涨薪,上一份的薪资,你就应该把你的福利也加上。比如你有绩效奖金,薪资调整都加上,得到一个范围值,不要告诉具体的数值。比如你薪资15k,你加上乱七八糟的,你可以说15-18k之间。 + +- 你认为每年的加薪幅度是多少? + + 提示:通常, 比较可靠的回答是: 你希望收入的增长和生活水平的提高保持一致。你还应该提到, + + 你的业绩将是加薪的主要因素 + + **求职者** : 总体来说,取决于我个人的业绩和公司的业绩(盈利状况)。但一般而言,至少和生活水平的提高保持一致。 + + + +在面试过程中,我们做的准备与实际遇到的问题总会有一些出入。记得大致的原则,巧妙的随机应变。懂得自己在市场中的定位,将对薪资的确定更为有利。 diff --git "a/\351\235\242\350\257\225/README.md" "b/\351\235\242\350\257\225/README.md" new file mode 100644 index 0000000..68134f1 --- /dev/null +++ "b/\351\235\242\350\257\225/README.md" @@ -0,0 +1,15 @@ +## 面试 + +这部分主要写关于面试的一些问题,如面对人事的关于离职原因的问题的回答,如何写一个简历?在面试的最后环节,怎么提问?以及一些自己搜集和整理的笔试题。 + +- [离职原因回答](https://github.com/xianyunyh/PHP-Interview/blob/master/%E9%9D%A2%E8%AF%95/01%E7%A6%BB%E8%81%8C%E5%8E%9F%E5%9B%A0%E5%9B%9E%E7%AD%94.md) + +- [写简历](https://github.com/xianyunyh/PHP-Interview/blob/master/%E9%9D%A2%E8%AF%95/02%E5%86%99%E7%AE%80%E5%8E%86.md) + +- [裸辞应对](https://github.com/xianyunyh/PHP-Interview/blob/master/%E9%9D%A2%E8%AF%95/03%E8%A3%B8%E8%BE%9E%E5%BA%94%E5%AF%B9.md) + +- [面试提问](https://github.com/xianyunyh/PHP-Interview/blob/master/%E9%9D%A2%E8%AF%95/03%E9%9D%A2%E8%AF%95%E6%8F%90%E9%97%AE.md) + +- [笔试题](https://github.com/xianyunyh/PHP-Interview/blob/master/%E9%9D%A2%E8%AF%95/%E7%AC%94%E8%AF%95%E9%A2%98.md) + + \ No newline at end of file diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" index 66212cc..ac44e8a 100644 --- "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" @@ -2,12 +2,14 @@ 1、**给你四个坐标点,判断它们能不能组成一个矩形,如判断([0,0],[0,1],[1,1],[1,0])能组成一个矩形。** -> ``` +> > 解题思路: -> * 以一个点为参考点。分别计算出到任意三点的距离,最长的距离的一个一个点是对角线的点。 -> * 剩下的两个点是左右两个点。 用剩下的两个点计算出另外一个对角线的距离。如果两条对角线相等, -> * 判断任意一个角是不是直角。即可判断是不是矩形了。 -> ``` +> +> 以一个点为参考点。分别计算出到任意三点的距离,最长的距离的一个一个点是对角线的点。 +> +> 剩下的两个点是左右两个点。 用剩下的两个点计算出另外一个对角线的距离。如果两条对角线相等, +> +> 判断任意一个角是不是直角。即可判断是不是矩形了。 - [php实现](https://github.com/xianyunyh/arithmetic-php/blob/master/package/Other/Square.php) @@ -79,14 +81,15 @@ cat access.log | awk '{arr[$4]++} END {for(i in arr) {print arr[i],$0}}' | sort b、列出早上10点访问量做多的20个url地址? ```shell - +cat access.log| awk '/2017:16/{arr[$1]++} END{ for(i in arr) {print ar +r[i],i}}' | sort -rn | head -20 ``` 6、**csrf和xss的区别** ``` csrf 跨站请求攻击。验证码、token、检测refer - xss **跨站脚本攻击**,过滤用户输入。 + xss 跨站脚本攻击,过滤用户输入。 ``` 7、**应用中我们经常会遇到在user表随机调取10条数据来展示的情况,简述你如何实现该功能**。 @@ -214,7 +217,7 @@ function getPoint(Node $a,Node $b) { 17、**isset(null) isset(false) empty(null) empty(false)输出** -​ false,false,true,true +​ false,true,true,true,[参考](http://php.net/manual/zh/types.comparisons.php) 18、**优化MYSQL的方法** @@ -234,8 +237,32 @@ var_dump($result['extension']); 21、参数为多个日期时间的数组,返回离当前时间最近的那个时间 +解题思路:让第一个时间作为哨兵。计算第一个时间和当前时间的差 diff,然后从第二个值开始遍历。如果第二个值和当前时间的值大于diff。则继续,否则该时间离当前时间近。把min赋值给当前值。 + +```php +function ($timeArray,$now) { + $min = $timeArray[0]; + $diff =abs($min-$now); + $length = count($timeArray); + for($i=1;$i<$length;$i++) { + $diff1 = $timeArray[$i] - $now; + if($diff1 < $diff) { + $diff = $diff1; + $min = timeArray[$i]; + } + } + return $min; +} +``` + + + 22、echo、print、print_r的区别 +``` +echo 是一个语法结构,print是一个函数 有返回值1.print_r 是一个函数,用于打印复合类型变量。 +``` + 23、http协议的header中有哪些key及含义 非常多,建议记住几个常见的。 @@ -343,6 +370,17 @@ O(n) 34、PHP的的这种弱类型变量是怎么实现的? +通过zval结构。 zval 包含变量的信息。 + +``` +zval{ + type + value +} +``` + +type 记录变量的类型。然后根据不同的类型,找到不同的value。 + 35、在HTTP通讯过程中,是客户端还是服务端主动断开连接? 36、PHP中发起http请求有哪几种方式?它们有何区别? @@ -353,16 +391,83 @@ O(n) ![img](01.png) -38、有两个文件文件,大小都超过了1G,一行一条数据,每行数据不超过500字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP最大允许内内为255M。 +38、有两个文本文件,大小都超过了1G,一行一条数据,每行数据不超过500字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP最大允许内内为255M。 + +```php +// 思路:使用协程yield +function readFieldFile($fileName) +{ + $fp = fopen($fileName, "rb"); + while (!feof($fp)) { + yield fgets($fp); + } + fclose($fp); +} + +$file1 = readFieldFile('big1.txt'); +$file2 = readFieldFile('big2.txt'); + +$file1->rewind(); +$file2->rewind(); +while ($file1->valid() && $file2->valid()) { + if ($file1->current() == $file2->current()) { + file_put_contents('big.txt', $file1->current(), FILE_APPEND); + } + $file1->next(); + $file2->next(); +} +``` 39、请写出自少两个支持回调处理的PHP函数,并自己实现一个支持回调的PHP函数 -40、请写出自少两个获取指定文件夹下所有文件的方法(代码或思路)。 +preg_match_callback. call_user_func + +```php +function myCallBack(Closure $closure, $a, $b) +{ + return $closure($a, $b); +} + +myCallBack(function ($a, $b) { + return $a + $b; +}, 1, 2); + +``` + +40、请写出至少两个获取指定文件夹下所有文件的方法(代码或思路)。 + +```php +// 递归获取,排除.和..,除了文件夹就是文件 +function myScanDir($dir) +{ + $files = array(); + if (is_dir($dir)) { + if ($handle = opendir($dir)) { + while (($file = readdir($handle)) !== false) { + if ($file != "." && $file != "..") { + if (is_dir($dir . "/" . $file)) { + $files[$file] = myScanDir($dir . "/" . $file); + } else { + $files[] = $dir . "/" . $file; + } + } + } + closedir($handle); + return $files; + } + } +} + +``` -41、请写出自少三种截取文件名后缀的方法或函数(PHP原生函数和自己实现函数均可) +41、请写出至少三种截取文件名后缀的方法或函数(PHP原生函数和自己实现函数均可) + +basename expload() strpos 42、PHP如何实现不用自带的cookie函数为客户端下发cookie。对于分布式系统,如何来保存session值。 +保持到redis中。 + 43、请用SHELL统计5分钟内,nginx日志里访问最多的URL地址,对应的IP是哪些? 44、写一段shell脚本实现备份mysql指定库(如test)到指定文件夹并打包,并删除30天前的备份,然后将新的备份推送到远端服务器,完成后送邮件通知。 @@ -373,6 +478,8 @@ O(n) 47、如何分析一条sql语句的性能。 +explain SQL + 48、ping一个服务器ping不通,用哪个命令跟踪路由包? linux:traceroute,windows:tracert @@ -380,7 +487,6 @@ linux:traceroute,windows:tracert 50、$a=[1,2,3]; foreach($a as &$v){} foreach($a as $v){} var_dump($a)等于多少; (我加的) -https://laravel-china.org/articles/7001/php-ray-foreach-and-references-thunder 51、数据库中的存放了用户ID,扣费很多行,redis中存放的是用户的钱包,现在要写一个脚本,将数据库中的扣费记录同步到redis中,每5分钟执行一次。请问要考虑哪些问题? 52、MYSQL主从服务器,如果主服务器是innodb引擎,从服务器是myisam引擎,在实际应用中,会遇到什么问题? @@ -416,9 +522,12 @@ https://blog.csdn.net/v_JULY_v/article/details/6279498 67、php-fpm各配置含义,fpm的daemonize模式 +``` static - 子进程的数量是固定的(pm.max_children) ondemand - 进程在有需求时才产生(当请求时,与 dynamic 相反,pm.start_servers 在服务启动时即启动 dynamic - 子进程的数量在下面配置的基础上动态设置:pm.max_children,pm.start_servers,pm.min_spare_servers,pm.max_spare_servers +``` + 68、让你实现一个简单的架构,并保持高可用,两个接口,一个上传一条文本,一个获取上传的内容,你怎么来设计?要避免单机房故障,同时要让代码层面无感。 69、两台mysql服务器,其中一台挂了,怎么让业务端无感切换,并保证正常情况下讲台服务器的数据是一致的 @@ -483,7 +592,7 @@ id username salary pid 我当时的答案是用链表来存,缓存命中就将该缓存移到链表头,然后链表尾就都是冷数据了。 我记得之前是在哪里看过这个设计,但我忘记在连接了,请知道朋友的把连接贴上来。 -95、==和===的区别,写出以下输出:"aa"==1,"bb"==0,1=="1" +95、`==` 和`===`的区别,写出以下输出:"aa"==1,"bb"==0,1=="1" 96、一个排序好的数组,将它从中间任意一个位置切分成两个数组,然后交换它们的位置并合并,合并后新数组元素如:20,21,22,25,30,1,2,3,5,6,7,8,15,18,19,写一个查询函数来查找某个值是否存在。 @@ -497,14 +606,24 @@ id username salary pid 101、描述autoload的机制 -102、mysql中字段类型各占几个字节:smallint、int、bigint、datetime、varchar(8) +102、mysql中字段类型各占几个字节:smallint【2】、int【4】、bigint【8】、datetime【4】、varchar(8) 103、哪些属性唯一确定一条TCP连接 +``` +源端口、目标端口、源ip、目标ip +``` + 104、myisam和innodb的区别,为什么myisam比innodb快,myisam和innodb的索引数据结构是什么样的?innodb主键索引和非主键索引的区别?其索引上存放的数据是什么样的? 105、断开TCP连接时,timewait状态会出现在发起分手的一端还是被分手的一端 +出现在分手的一端。time_wait会持续2mls。由于网络的不稳定等因素,会导致ack发送失败。在2MLS内,可以重发。 + +1. 被动关闭连接的一方在一段时间内没有收到对方的ACK确认数据包,会重新发送FIN数据包,因而主动关闭连接的一方需要停留在等待状态以处理对方重新发送的FIN数据包。否则他会回应一个RST数据包给被动关闭连接的一方,使得对方莫名其妙。 + +2. 在TIME_WAIT状态下,不允许应用程序在当前ip和端口上和之前通信的client(这个client的ip和端口号不变)建立一个新的连接。这样就能避免新的连接收到之前的ip和端口一致的连接残存在网络中的数据包。这也是TIME_WAIT状态的等待时间被设置为2MSL的原因,以确保网络上当前连接两个方向上尚未接收的TCP报文已经全部消失。 + 106、AWK各种数据分析考得非常多,要多练习,题目不再一一写了 107、redis中集合、有序集合、hyperLog、hash的数据结构是啥样的 diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2302.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2302.md" index f4b37f3..629334b 100644 --- "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2302.md" +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2302.md" @@ -4,7 +4,7 @@ 1. TCP报头格式 - ![图片加载中](http://img.blog.csdn.net/20170227111849763?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTWFyeTE5OTIwNDEw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) + ![img](http://s1.51cto.com/images/20171216/1513399302449506.png) 端口号用来标识不同的应用程序。 @@ -41,13 +41,13 @@ - 5、首部长度:首部中32bit字的数目,可表示15*32bit=60字节的首部。一般首部长度为20字节。 - 6、数据 -1. TCP/UDP区别(不仅是宏观上的,最好能根据各自的机制讲解清楚) +3. TCP/UDP区别(不仅是宏观上的,最好能根据各自的机制讲解清楚) TCP存在三次握手。能进行流量控制,保证数据的完整。 UDP不存在握手。会导致丢包。传输比较快。 -2. HTTP状态码(最好结合使用场景,比如在缓存命中时使用哪个) +4. HTTP状态码(最好结合使用场景,比如在缓存命中时使用哪个) 2xx 标识正常 @@ -57,13 +57,13 @@ 5xx 服务器内部错误 -3. HTTP协议(一些报头字段的作用,如cace-control、keep-alive) +5. HTTP协议(一些报头字段的作用,如cace-control、keep-alive) -4. OSI协议、TCP/IP协议以及每层对应的协议。 +6. OSI协议、TCP/IP协议以及每层对应的协议。 -5. SESSION机制、cookie机制 +7. SESSION机制、cookie机制 -6. TCP三次握手、四次挥手(这个问题真的要回答吐了,不过真的是面试官最喜欢问的,建议每天手撸一遍,而且不只是每次请求的过程,各种FIN_WAIT、TIME_WAIT状态也要掌握)。 +8. TCP三次握手、四次挥手(这个问题真的要回答吐了,不过真的是面试官最喜欢问的,建议每天手撸一遍,而且不只是每次请求的过程,各种FIN_WAIT、TIME_WAIT状态也要掌握)。 [TCP详解](https://github.com/xianyunyh/tcp-ip-protocal) @@ -80,11 +80,15 @@ - 服务器再发送FIN,序号为N的数据包 进入LAST_ACK - 客户端收到后,进入TIME_WAIT,再发送ACK=1.ack=N+1的数据包 服务端进入 CLOSED -1. 打开网页到页面显示之间的过(涵盖了各个方面,DNS解析过程,Nginx请求转发、连接建立和保持过程、浏览器内容渲染过程,考虑的越详细越好)。 -2. http和https区别,https在请求时额外的过程,https是如何保证数据安全的 -3. IP地址子网划分 -4. POST和GET区别 -5. DNS解析过程 +9. 打开网页到页面显示之间的过(涵盖了各个方面,DNS解析过程,Nginx请求转发、连接建立和保持过程、浏览器内容渲染过程,考虑的越详细越好)。 + +10. http和https区别,https在请求时额外的过程,https是如何保证数据安全的 + +11. IP地址子网划分 + +12. POST和GET区别 + +13. DNS解析过程 **深入部分** diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2303.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2303.md" new file mode 100644 index 0000000..006c2e7 --- /dev/null +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2303.md" @@ -0,0 +1,277 @@ +## PHP + +1. 写一段代码保证多个进程同时写入成功 + +```php +#加锁 +function write($filepath,$data) { + $fp = fopen( $filepath, 'a' ); //以追加的方式打开文件,返回的是指针 + do{ +    usleep( 100 );       //暂停执行程序,参数是以微秒为单位的 + }while( !flock( $fp, LOCK_EX ) );  //以独享写入的方式锁定文件,成功则返回TRUE,否则FALSE +} + $res = fwrite( $fp, $data."/n" );  // 以追加的方式写入数据到打开的文件 + flock( $fp, LOCK_UN );      //解锁,以让别的进程进行锁定 + fcloce( $fp );           //关闭打开的文件指针 + return $res;            //返回写入结果 +} + +``` + +[https://www.cnblogs.com/gengyi/p/6399206.html](https://www.cnblogs.com/gengyi/p/6399206.html) + +2. 打出前一天的时间 + +```php +date('Y-m-d H:i:s', strtotime('-1 day')) +``` + +3. echo、print、print_r的 区别 + +echo 是一个语法结构。用于输出基本变量信息。print是一个函数。有返回值。print_r是一个函数。一般用来打印复合类型信息。 + +4. 翻转字符【包含中文】 + +```php + function strRev($str,$encoding='utf-8'){ + $result = ''; + $len = mb_strlen($str); + for($i=$len-1; $i>=0; $i--){ + $result .= mb_substr($str,$i,1,$encoding); + } + return $result; + } +``` + +5. 遍历一个文件夹下的所有文件和子文件 + +```php +function my_dir($dir) { + $files = array(); + if(@$handle = opendir($dir)) { //注意这里要加一个@,不然会有warning错误提示:) + while(($file = readdir($handle)) != = false) { + if($file != ".." && $file != ".") { //排除根目录; + if(is_dir($dir."/".$file)) { //如果是子文件夹,就进行递归 + $files[$file] = my_dir($dir."/".$file); + } else { //不然就将文件的名字存入数组; + $files[] = $file; + } + + } + } + closedir($handle); + return $files; + } +} +``` + +6. 不适用第三个变量交换两个变量的值 + +```php +function swap (int &$a, int &$b){ + // 20 10 + $a = $a+$b; // 30 + $b = $a-$b; //20 a + $a = $a-$b; +} +``` + +7. XSS、CSRF、SQL注入 + +XSS 跨站脚本攻击。通常是由于没有过滤用户的输入导致的。 + +CSRF 跨站请求攻击。 + +SQL注入。用户的输入非法字符 和后端的sql组成了危险的sql语句 + +8. PHP实现单例模式 +9. MVC的认识 + + + +10. $_SERVER 常用字段的意思 +11. cookie 和session之间的关系 +12. GET和POST区别 +13. 接口和抽象类的区别 +14. TCP/IP HTTP +15. 进程、协程、线程 +16. 进程通信的方式 +17. 并发编程模型 + - 多进程 + - 多线程 + - epoll +18. epoll select poll区别 +19. 二维数组排序 + +## Linux + +linux计划任务 + +在每天凌晨1点运行 a.sh文件 + +0 1 * * * sh a.sh + +每隔五分钟运行一次 + +5/* * * * * sh a.sh + +每周一当前13:00每隔五分钟运行一次 + +5/* 13 * * 1 sh a.sh + +每月的1号和15号每隔两个小时执行一次 + +`* */2 1,15 * sh a.sh` + + + +## mysql + +1. **Myisam和innodb的差别** + +存储结构。myisam一个表,三个文件。结构、数据、索引 + +innodb 两个文件。索引、数据保持在一个文件。 + +innodb支持事务、myiam不支持 + +innodb支持外键、支持行锁 + +2. **优化数据库的方法** + +- 选择正确的存储引擎 +- 选择合适的表字段类型 +- 适当的增加索引 +- 增加服务器的硬件 +- 分库分表 +- 读写分离 + +2. **数据库的事务** +3. **写出发帖数最多的十个人的名** + +posts(id,username,content) id帖子序号、username 用户名、content内容 + +```sql +select count(id) as ct from posts group by username order by ct desc limit 10; +``` + +4. **有一个数据表user需要按 uid 10,32,22,76,13的顺序检索出来** + +```sql +select * from user where uid =10 union select * from user where uid =32 union select * from user where uid =22 union select * from user where uid =22 union select * from user where uid =76 union select * from user where uid =13 +``` + +5. user表记录学生的信息。有uid,和name,subject表是科目。主要subject_name,subject_id. score表是记录学生各科的成绩,有uid,subject_id,score(分数),example_id(学期) + + - 写出每个学生各科目的所有成绩的平均成绩 + + ```sql + select avg( `score`) from score group by uid + ``` + + - 查出科目id为10的,所有平均分排名前十的学生 + + ```sql + select avg(`score`) as avg_number from score group by uid subject_id having (subject_id =10 ) limit 10 + ``` + + - 平均成绩大于300的学生的学号 + + ```sql + select avg(`score`) as avg_number,uid from score where avg_number > 300 + ``` + +6. mysql 查询一个数据库中的所有的表名 + + ```sql + select table_name + from information_schema.tables + where table_schema='当前数据库' + ``` + + + +7. mysql 日期函数 + +## 算法 + +- 翻转数组 + +```php +/** + * 反转数组 + * @param array $arr + * @return array + */ +function reverse($arr) +{ + $n = count($arr); + + $left = 0; + $right = $n - 1; + + while ($left < $right) { + $temp = $arr[$left]; + $arr[$left++] = $arr[$right]; + $arr[$right--] = $temp; + } + + return $arr; +} +``` + +- 约瑟夫环 + +```php +function yuesefu($n,$m) { + $r=0; + for($i=2; $i<=$n; $i++) { + $r=($r+$m)%$i; + } + return $r+1; +} +``` + +- 二分查找【如何在有序的数组中找到一个数的位置】 + +```php +/** + * 二分查找 + * @param array $array 数组 + * @param int $n 数组数量 + * @param int $value 要寻找的值 + * @return int + */ +function binary_search($array, $n, $value) +{ + $left = 0; + $right = $n - 1; + + while ($left <= $right) { + $mid = intval(($left + $right) / 2); + if ($value > $array[$mid]) { + $right = $mid + 1; + } elseif ($value < $array[$mid]) { + $left = $mid - 1; + } else { + return $mid; + } + } + + return -1; +} +``` + +## REDIS + +Memcache +该产品本身特别是数据在内存里边的存储,如果服务器突然断电,则全部数据就会丢失 +单个key(变量)存放的数据有1M的限制 +存储数据的类型都是String字符串类型 +本身没有持久化功能 +可以使用多核(多线程) +Redis +数据类型比较丰富:String、List、Set、Sortedset、Hash +有持久化功能,可以把数据随时存储在磁盘上 +本身有一定的计算功能 +单个key(变量)存放的数据有1GB的限制 \ No newline at end of file diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" new file mode 100644 index 0000000..b2237af --- /dev/null +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" @@ -0,0 +1,68 @@ +# 面试题 + +1. "\t" "\r" "\n" "\x20" + + 答:\t tab \r 换行 \n 回车 \x20 16进制 + +2. setcookie("user","value") var_dump($_COOKIE['user']) + + 答:null + +3. `$a = 3; echo "$a",'$a',"\\\$a","$a"."$a","$a"+"$a"` + + 3$a\$a336 + +4. require include区别 + + require 遇到错误会中断,include不会 + +5. private protected public const static + +6. 调用父类的方法`parent::` + +7. `$__GET,` `$_POST` + +8. php 跳转页面 + + header("location:"); + +9. 客户端ip整型 + + ip2long + +10. 什么是php-fpm + + FastCGI进程管理器 + +11. 查看进程打开的文件 + + lsof + +12. 查看io的状态 + + iostat + +13. 查看cpu的负载 + + top + +14. mysql 的增删改查 + +15. 聚簇索引所在的列数据是乱序的,会有什么影响。 + + [https://www.cnblogs.com/starhu/p/6406495.html](https://www.cnblogs.com/starhu/p/6406495.html) + +16. csrf xss sql注入 + +17. mysql主从同步的原理,什么sql语句导致mysql的主从同步失败 + + mysql binlog + +18. 拓展 活动高峰时期,cpu出现100%。你如何排查。nginx+php-fpm 数据读取redis和mysql、 + + + +19. 项目中遇到了什么问题, +20. 之前的项目有没有其他的优化方案? +21. 平时怎么学习的? +22. 未来的职业的职业方向? diff --git "a/\351\235\242\350\257\225/\351\235\242\350\257\225\346\200\273\347\273\223.md" "b/\351\235\242\350\257\225/\351\235\242\350\257\225\346\200\273\347\273\223.md" new file mode 100644 index 0000000..306bcae --- /dev/null +++ "b/\351\235\242\350\257\225/\351\235\242\350\257\225\346\200\273\347\273\223.md" @@ -0,0 +1,45 @@ +参加了一次的面试,和面试官聊的还可以,最后让面试官对我简单评价下和给我一点建议,对方说我技术栈比较全面,但是可能缺少大型项目的锻炼,说的很对。大型项目可是自己的短板,理论的东西没有实战,只是理论。下面就记录下面试的问题。 + + + +1. 你们之前的项目的pv、uv是多少? + +这个我当时实话实说了,pv并不高,高并发不存在。架构简单,因为不想瞎扯,吹那么多 + +2. 之前项目有没有做过集群 + +这个确实做过,无论是采用的阿里云的负载均衡还是自己nginx搭建的反向代理和自己用lvs+keeplived做的实验,都说了 + +3. 做了集群,后端的机器如何拿到前面访问的真实IP + +这个通过添加header头的方式 + +4. 协成了解过吗?和线程的区别 + +用户态线程,由程序控制,开销小,线程CPU直接调度,CPU时钟切换,耗费资源 + +5. 分库分表做过没?采取什么样的方式 + +通过区间范围、hash + +6. mysql分区表了解过没 + +了解过,但是这种一般不推荐用 + +7. sso单点登陆有哪些方案 + +这个我当时想的是统一中心认证,使用jsonp拉取登陆信息。 + +8. 主流的框架,都看过源码没? + +看过thinkphp laravel的 + +9. swoole了解过没?使用过没? + +这个了解过,真实项目未使用。 + +10. golang的协程、python的协程,的区别 + +golang的协程goruntine + +11. 对加班怎么看? \ No newline at end of file diff --git "a/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2305.md" "b/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2305.md" new file mode 100644 index 0000000..a8a82c6 --- /dev/null +++ "b/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2305.md" @@ -0,0 +1,74 @@ +今天去参加了一个面试,然后总共就4个题目,记录下,给大家一点启发。 + +### 1. 查询订单表(order)中,累计支付金额大于200的用户 + +| uid | amount| +| :------------ | :------------ | +| 1 | 10 | +| 2| 10 | + +```sql +select sum(amount) as ct,uid from orders group by uid having ct > 200 +``` +### 2. 查询一个表中的重叠数据。 + +| id | uid | start | end | +| ------------ | ------------ | ------------ | ------------ | +| 1| 1 | 2020-02-01 | 2020-03-01 | +| 2 | 1 | 2020-02-04 |2020-04-03 | +| 3 | 2 | 2020-01-01 | 2020-02-01 | +| 4 | 2 | 2020-02-02 | 2020-05-01 | + +解释: 在上述表中,`uid=1`的记录 在时间段上有重叠, id=1的记录和`2020-02-01` - `2020-03-01` 和 id = 2的记录 `2020-02-04` - `2020-04-03`。 +写一个sql实现,找出找个重叠的两条数据。 + +- 利用自联结 +- 出现重叠数据一条记录的开始时间落在的另外一条记录的start和end之间 + +```sql +SELECT + a.uid, + a.`start` AS a_start, + b.`start` AS b_start, + a.`end` AS a_end, + b.`end` AS b_end +FROM + log AS a +LEFT JOIN log AS b ON a.uid = b.uid +WHERE + a.id <> b.id +AND ( + (a.`start` > b.`start` && a.`start` < b.`end`) or (b.`start` > a.`start` && b.`start` < a.end ) +) +ORDER BY + a.id ASC +``` + +### 3. 实现一个函数,判断给定的字符串是不是合法? + ```php + function isEmail(array $string): bool { + + } + ``` +1. 假设给定的字符串是一个字符数组 `['a','b'....]` +2. 不允许使用正则表达式 +3. 你只能使用以下三个函数 `empty`、`foreach`、`array_shift` +4. 假定给定的字符中只有**小写字母[a-z]**、**\@**、**\.** 这三种字符 +正确的邮箱:`test@test.com` +不是邮箱的字符:`@.`、`.@`、`@@`、`a@test.cc.c`、`aac@..a` + +### 4. 防止超卖 +商品表(goods) +- good_id 商品id +- num 库存 +| good_id | num | +| ------------ | ------------ | +| 1 | 1 | + + ```php + $dbh->beginTransaction(); + //加锁 + $dbh->execute("update goods set num = num -1 where goods_id =1") + $dbh->commit(); + ``` +上面的代码有啥问题?,如何解决? diff --git "a/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2306.md" "b/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2306.md" new file mode 100644 index 0000000..6f19a7a --- /dev/null +++ "b/\351\235\242\350\257\225/\351\235\242\350\257\225\351\242\2306.md" @@ -0,0 +1,22 @@ +## 实现version_compare函数。 + +1. 假设给你的两个版本号里面,都不为空 +2. 字符串中只包含数字和小数点. +3. 如果a >b 返回 1 b > a 返回-1,其他情况返回0 +```php + function version_compare(string $v1,string $v2): int{ + $v1Arr = explode('.',$v1); + $v2Arr = explode('.',$v2); + $len = count($v1Arr) > count($v2Arr) ? count($v1Arr) : count($v2Arr); + for ($i = 0; $i < $len;$i++) { + $n = (int)($v1Arr[$i] ?? 0); + $m = (int) ($v2Arr[$i] ?? 0); + if ($n > $m) { + return 1; + } else if ($n < $m) { + return -1; + } + } + return 0; + } +```