# PHP 面试要点

## 简单做一下自我介绍?  介绍一下三年内最得意的项目？

## **操作系统**

* IO 多路复用是什么？有哪些 api？
* select 和 epoll 的区别？水平触发和边缘触发的区别是啥？使用的时候需要注意什么？
  * select：fd\_setsize有限制1024，fd基于数组存储，每次调用都要将fd集合从用户态拷贝到内核态，每次调用都需要在内核中遍历所有的fd
  * poll：本质和select没有区别，fd\_size不限制，原因是fd是基于链表存储的
  * epoll：fd\_size不限制，fd基于红黑树存储，同时每一次通过epoll\_ctl来将fd加入内核中&#x20;
* epoll 储存描述符的数据结构是什么？
* select 有描述符限制吗？是多少？
* 进程 / 线程 / 协程区别？go 和 swoole 的协程实现有啥区别？（分配资源的基本单位 / 运行和调度的基本单位 / 用户线程，M:N 模型和 N:1 模型）

## **计算机网络**

* TCP 三次握手的流程
* 客户端发送一个 SYN 标志位置 1 的包，指明客户端要连接服务器端的接口，发送完毕后，客户端进入 SYN\_SEND 状态
* 服务器发回确认包 (ACK) 应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号，放到 Seq 域里，同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1，即X+1。 发送完毕后，服务器端进入 SYN\_RCVD 状态。
* 客户端再次发送确认包(ACK)，SYN 标志位为0，ACK 标志位为1，并且把服务器发来 ACK 的序号字段+1，放在确定字段中发送给对方，并且在数据段放写ISN的+1
* TCP 三次握手和四次挥手，为什么三次握手，为什么四次挥手 在Tcp连接中，服务端的SYN和ACK向客户端发送是一次性发送的，而在断开连接的过程中，B端向A端发送的ACK和FIN是分两次发送的。因为在B端接收到A端的FIN后，B端可能还有数据要传输，所以先发送ACK，等B端处理完自己的事情后就可以发送FIN断开连接了
* 一次完整的 HTTP 请求（从浏览器输入 www.baidu.com 到加载出页面发生了什么） 1. dns 解析 ip port 2. 建立 tcp 连接 3. 发送 HTTP 报文 4. 接收 HTTP 报文 5. 浏览器解析显示 6. 断开 tcp 连接
* HTTP 协议包括哪些请求？ 1. GET：请求读取由URL所标志的信息。 2. POST：给服务器添加信息（如注释）。 3. PUT：在给定的URL下存储一个文档。 4. DELETE：删除给定的URL所标志的资源。
* GET 和 POST 区别 1. GET在浏览器回退时是无害的，而POST会再次提交请求。 2. GET产生的URL地址可以被Bookmark，而POST不可以。 3. GET请求会被浏览器主动cache，而POST不会，除非手动设置。 4. GET请求只能进行url编码，而POST支持多种编码方式application/x-www-form-urlencoded，multipart/form-data，application/json，text/xml 5. GET请求参数会被完整保留在浏览器历史记录里，而POST中的参数不会被保留。 6. GET请求在URL中传送的参数是有长度限制的，而POST没有。 7. 对参数的数据类型，GET只接受ASCII字符，而POST没有限制。 8. GET比POST更不安全，因为参数直接暴露在URL上，所以不能用来传递敏感信息。 9. GET参数通过URL传递，POST放在Request body中。 10. GET产生一个TCP数据包，POST产生两个TCP数据包。GET请求将http header和data一起发送，POST请求先发header，再发送data，返回数据
* Cookie存在哪
  * 如果设置了过期时间，Cookie存在硬盘里
  * 如果没有设置过期时间，Cookie存在内存里（会随着浏览器的关闭而消失）
* Session 和 Cookie 的区别，禁用 Cookie 后怎么办 1. COOKIE保存在客户端，而SESSION则保存在服务器端 2. 从安全性来讲，SESSION的安全性更高 3. 从保存内容的类型的角度来讲，COOKIE只保存字符串（及能够自动转换成字符串） 4. 从保存内容的大小来看，COOKIE保存的内容是有限的，比较小，而SESSION基本上没有这个限制 5. 从性能的角度来讲，用SESSION的话，对服务器的压力会更大一些 6. SEEION依赖于COOKIE，但如果禁用COOKIE，也可以通过url传递

## **PHP**

* PHP执行过程
  * 扫描：将index.php内容变成一个个语言片段（token）
  * 解析：将一个个语言片段有意义的表达式
  * 编译：将表达式编译成中间码（opcode）
  * 执行：将中间码一条条的执行
  * 输出：将要输出的内容输出到缓冲区
* nginx中php-fpm的socket是什么类型。
  * unix socket适合nginx和php-fpm都在一台机器上
  * tcp socket适合nginx和php-fpm不在同一台机器上
* PHP 的解析方式有哪几种？cgi、fastcgi、php-fpm 区别
  * cgi：公共网关接口，http服务器发起请求，会启动对应的实现了cgi协议的php解析器，php解析器解析php.ini，初始化执行环境，并处理请求，在以cgi规定的格式返回结果，推出进程。
  * fast-cgi：由cgi发展而来，是一种进程池，先启动一个master进程，解析php.ini和初始化执行环境，然后启动多个worker进程，php请求发到master进程，通过master进程分发的到worker进程进行处理
  * php-fpm：fastcgi进程管理器，用于替换php内核的fastcgi大部分附加功能
* 描述一下 cli 模式下的几个生命周期？ 1. 模块初始化阶段 (Module init)：即调用每个拓展源码中的的 PHP\_MINIT\_FUNCTION 中的方法初始化模块，进行一些模块所需变量的申请，内存分配等。 2. 请求初始化阶段 (Request init)：即接受到客户端的请求后调用每个拓展的 PHP\_RINIT\_FUNCTION 中的方法，初始化 PHP 脚本的执行环境。 3. 执行该 PHP 脚本。 4. 请求结束 (Request Shutdown)：这时候调用每个拓展的 PHP\_RSHUTDOWN\_FUNCTION 方法清理请求现场，并且 ZE 开始回收变量和内存 5. 关闭模块 (Module shutdown)：Web 服务器退出或者命令行脚本执行完毕退出会调用拓展源码中的 PHP\_MSHUTDOWN\_FUNCTION 方法
* PHP7 为什么比 PHP5 性能提升了 1. 变量存储字节减小，减少内存占用，提升变量操作速度 2. 改善数组结构，数组元素和 hash 映射表被分配在同一块内存里，降低了内存占用、提升了 cpu 缓存命中率 3. 改进了函数的调用机制，通过优化参数传递的环节，减少了一些指令，提高执行效率
* PHP7 与 PHP5 1. 性能提升：PHP7 比 PHP5.0 性能提升了两倍。 2. 全面一致的 64 位支持。 3. 以前的许多致命错误，现在改成 \[抛出异常]。 4. PHP 7.0 比 PHP5.0 移除了一些老的不在支持的 SAPI（\[服务器端] 应用编程端口）和扩展。 5. PHP 7.0 比 PHP5.0 新增了空接合操作符。 6. PHP 7.0 比 PHP5.0 新增加了结合比较运算符。 7. PHP 7.0 比 PHP5.0 新增加了函数的返回类型声明。 8. PHP 7.0 比 PHP5.0 新增加了标量类型声明。 9. PHP 7.0 比 PHP5.0 新增加匿名类。
* GC（垃圾回收） 的出现是为了解决什么问题？什么时候会触发 GC？说下大概流程 1. PHP 5.3 版本之前都是采用引用计数的方式管理内存 2. PHP 所有的变量存在一个叫 zval 的变量容器中，当变量被引用的时候，引用计数会+1，变量引用计数变为0时，PHP 将在内存中销毁这个变量 3. 引用计数中的循环引用，引用计数不会消减为 0，就会导致内存泄露 4. 在 5.3 版本之后，做了这些优化： 1. 并不是每次引用计数减少时都进入回收周期，只有根缓冲区满额后在开始垃圾回收； 2. 可以解决循环引用问题； 3. 可以总将内存泄露保持在一个阈值以下。
* 如何解决 PHP 内存溢出问题 1. 增大 PHP 脚本的内存分配 2. 变量引用之后及时销毁 3. 将数据分批处理
* php-fpm 运行机制？（master 管理，worker 循环 accept）
* php-fpm 模式下，kill -9 master-pid，会怎么样？kill matser-pid 呢？（信号机制）
* 内存分配流程？为什么要这么设计？
* php 里的数组是怎么实现的？（这里要注意下 php5 和 php7 实现的区别，优化了非常多）
* nginx 和 php-fpm 的通信机制？fast-cgi 和 cgi 区别？
* php-fpm 创建 worker 进程的规则是什么？不同场景下怎么选择？
* php 和 mysql 的通信机制？长链接和短链接啥区别？怎么实现的？连接池要怎么实现？
* swoole 协程的原理？（遇到阻塞时引发 php 栈和 c 栈的切换，细节可以参考下我的文章）
* 依赖注入是什么？如何实现的？能解决什么问题？（代码层面不再依赖具体实现，解耦）

## **mysql**

*   MySQL主要的存储引擎MyISAM和InnoDB的不同之处？

    解答思路：可以从五个方向去介绍。

    &#x20; 事务的支持不同（InnoDB支持事务、MyISAM不支持事务）；

    &#x20; 锁粒度（InnoDB行锁应用、MyISAM表锁）；

    &#x20; 存储空间（InnoDB既缓存索引文件，又缓存数据文件，MyISAM只能缓存索引文件）；

    &#x20; 存储结构（MyISAM：数据文件的扩展名为.MYD myData，索引文件的扩展名是.MYI myIndex；InnoDB：所有的表都保存在同一个数据文件里面，即.Ibd）；

    &#x20; 统计记录行数（MyISAM：保存有表的总行数，select count() from table会直接取出该值；InnoDB：没有保存表的总行数，select count() from table会遍历整个表，消耗相当大）。
*   Mysql 主从同步了解吗？ 1. MySQL之间的数据复制的基础是二进制日志文件binlog 2. 台MySQL数据库一旦启用二进制日志后，其作为master，它的数据库中所有操作都会以“事件”的方式记录在二进制日志中， 3. 其它数据库作为slave通过一个I/O线程与主服务器保持通信，并监控master的二进制日志文件的变化， 4. 如果发现master二进制日志文件发生变化，则会把变化复制到自己的中继日志中，然后slave的一个SQL线程会把相关的“事件”执行到自己的数据库中，以此实现从数据库和主数据的一致性，也就实现了主从复制 (主服务器把数据更新记录到二进制日志中，从服务器通过I/O thread向主库发起binlog请求，主服务器通过I/O dump thread把二进制日志传递给从库，从库通过I/O thread记录到自己的中继日志中。然后通过SQL thread应用中继日志中SQL的内容。)

    **如何查看机器运行的建康指标，如：CPU的LOAD和使用率，内存使用率，磁盘使用率，IOWAIT等，除此之外还有那些重要指标可以用来分析特定场景问题？**

    > yum install iotop iftop iostat htop 安装一系列工具 htop是top命令的加强版，可以看到cpu，内存，执行的进程等信息
* iftop 是监控网卡的
* iotop 是监控io的
* free -m 监控可用内存
* uptime 查看当前系统负载
* df -hl 查看磁盘
* 安装smart 工具yum install -y smartmontools,查看磁盘的smart信息smartctl -a /dev/sda
* 查看cpu信息cat /proc/cpuinfo
*   查看内存信息 cat /proc/meminfo

    **nginx**
* LVS 和 Nginx 分别作用在 osi 哪一层？
* 负载均衡算法 1. 轮询（默认）每个请求按时间顺序逐一分配到不同的后端服务器，如果后端服务器 down 掉，能自动剔除。 2. Weight 指定轮询几率，weight 和访问比率成正比， 用于后端服务器性能不均的情况。 3. ip\_hash 每个请求按访问 ip 的 hash 结果分配，这样每个访客固定访问一个后端服务器，可以解决 session 共享的问题。 4. fair（第三方）按后端服务器的响应时间来分配请求，响应时间短的优先分配。 5. url\_hash（第三方）

## **安全防范**

* CSRF 是什么？如何防范？\
  1\. CSRF（Cross-site request forgery）通常被叫做「跨站请求伪造」，可以这么理解：攻击者盗用用户身份，从而欺骗服务器，来完成攻击请求。 2. 防范措施： 1. 使用验证码 2. 给每一个请求添加令牌 token 并验证 只使用json api（js 发起ajax请求是限制跨域的） 禁用cross-origin（只允许options、head、get方法跨域请求） 检验referer头部 GET总是幂等 避免使用POST 不要复写方法 不要兼容旧浏览器 CSRF Tokens
* XSS 是什么？如何防范？ 1. XSS(Cross Site Scripting)，跨站脚本攻击，攻击者往 Web 页面里插入恶意 Script 代码，当用户浏览该页之时，嵌入其中 Web 里面的 Script 代码会被执行，从而达到恶意攻击用户的目的。 2. 防止 XSS 攻击的方式有很多，其核心的本质是：永远不要相信用户的输入数据，始终保持对用户数据的过滤 3. 将重要的cookie标记为http only，这样的话Javascript 中的document.cookie语句就不能获取到cookie了
* 什么是 SQL 注入？SQL注入产生的原因？如何防范？ 1. SQL注入产生的原因： 1. 程序开发过程中不注意规范书写SQL语句和对特殊字符进行过滤，导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行 2. 防范措施 1. 保持对用户数据的过滤（严格检查输入变量的类型和格式） 2. 过滤和转义特殊字符比如addslashes()函数 3. 利用mysql的预编译机制

1、为什么使用redis （一）性能 我们在碰到需要执行耗时特别久，且结果不频繁变动的SQL，就特别适合将运行结果放入缓存。这样，后面的请求就去缓存中读取，使得请求能够迅速响应。 （二）并发 在大并发的情况下，所有的请求直接访问数据库，数据库会出现连接异常。这个时候，就需要使用redis做一个缓冲操作，让请求先访问到redis，而不是直接访问数据库。 2.使用redis有什么缺点 (一)缓存和数据库双写一致性问题 (二)缓存雪崩问题 (三)缓存击穿问题 (四)缓存的并发竞争问题 3、单线程的redis为什么这么快 (一)纯内存操作 (二)单线程操作，避免了频繁的上下文切换 (三)采用了非阻塞I/O多路复用机制 参照上图，简单来说，就是。我们的redis-client在操作的时候，会产生具有不同事件类型的socket。在服务端，有一段I/0多路复用程序，将其置入队列之中。然后，文件事件分派器，依次去队列中取，转发到不同的事件处理器中。 4、redis的数据类型，以及每种数据类型的使用场景 回答：一共五种 (一)String 这个其实没啥好说的，最常规的set/get操作，value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。 (二)hash 这里value存放的是结构化的对象，比较方便的就是操作其中的某个字段。博主在做单点登录的时候，就是用这种数据结构存储用户信息，以cookieId作为key，设置30分钟为缓存过期时间，能很好的模拟出类似session的效果。 (三)list 使用List的数据结构，可以做简单的消息队列的功能。另外还有一个就是，可以利用lrange命令，做基于redis的分页功能，性能极佳，用户体验好。本人还用一个场景，很合适—取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队，先进先出的原则。 (四)set 因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重？因为我们的系统一般都是集群部署，使用JVM自带的Set，比较麻烦，难道为了一个做一个全局去重，再起一个公共服务，太麻烦了。 另外，就是利用交集、并集、差集等操作，可以计算共同喜好，全部的喜好，自己独有的喜好等功能。 (五)sorted set sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用，取TOP N操作。

* Redis 如何实现持久化
  1. RDB 持久化，将 Redis 在内存中的的状态保存到硬盘中，相当于备份数据库状态。
  2. AOF 持久化（Append-Only-File），AOF 持久化是通过保存 Redis 服务器锁执行的写状态来记录数据库的。相当于备份数据库接收到的命令，所有被写入 AOF 的命令都是以 Redis 的协议格式来保存的
