PHP面试总结

294 阅读4分钟

前言

只适合工作(1-3)年的PHPer

写简历的网站:

超级简历 推荐

冷熊简历 刚开始我用的这个,后来感觉太过于简单,不过程序员也不需要什么花里胡哨的简历,如果不喜欢第一个可以用这个

PHP

基础

PHP生命周期

  1. 词法分析:将PHP文件转换成 Token
  2. 语法分析:根据生成的 Token 和语法规则进行分析
  3. Zend 引擎:将代码编译为 opcode 后并执行
  4. 调用 SAPI 输出函数返回执行结果

SAPI 是什么

SAPI 是服务器应用编程接口,作为应用层(比如 Apache、Nginx、CLI等)和 PHP 交互数据的入口

SAPI 实现了和各种应用层的兼容,应用层可以根据自身情况定制 SAPI,例:

  1. apache2handler 和 apache2filter,这是提供给 Apache mod_php 的 SAPI;
  2. cgi,Web Server fork 出 cgi 进程使用的 sapi;
  3. fastcgi,Web Server 采用网络通信或者网络 IPC 和 PHP 交换数据的 SAPI;
  4. cli,命令行方式运行 PHP 脚本的 SAPI

Session和Cookie的区别

Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。

Coookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

参考

Session和Cookie的区别

PHP7跟PHP5的区别,具体多了哪些新特性?

  1. 性能提升了两倍
  2. 增加了结合比较运算符和??运算符
  3. 增加了标量类型声明、返回类型声明(declare(strict_types=1):严格模式)
  4. try…catch增加了多条件判断,可以对更多的Error错误进行异常处理
  5. 增加了匿名类,可以直接使用new class来实例化一个匿名类
  6. define可定义常量数组

参考

PHP7新特性

PHP 标量类型与返回值类型声明

魔术常量

__LINE__	  当前行号
__DIR__		  当前目录
__FILE__	  当前文件完整的文件地址
__FUNCTION__  当前函数名
__METHOD__	  类的方法榠
__CLASS__	  类名
__NAMESPACE__ 当前命名空间名称

魔术方法

__construct、__destruct、__call、__callStatic、__get、__set、__isset、
__unset、__toString、__set_state、__debuginfo、__clone、__sleep、__wakeup、__invoke

参考

PHP魔术方法

include 和 require 的区别

它们在加上 _ones 后缀时表示已加载文件的不加载

  1. 加载失败的处理方式不同

    require 加载失败,会报出一个 Fatal error 脚本停止执行

    include 加载失败,会报出一个 Warning 脚本会继续执行

  2. 性能不同

    include 执行文件每次都要进行读取和评估;

    require 文件只处理一次

    如果可能执行多次的代码,就用 require 效率比较高

简述一下 PHP 垃圾回收机制(GC)

PHP 5.3 版本之前都是采用引用计数的方式管理内存,PHP 所有的变量存在一个叫 zval 的变量容器中,当变量被引用的时候,引用计数会+1,变量引用计数变为0时,PHP 将在内存中销毁这个变量。

但是引用计数中的循环引用,引用计数不会消减为 0,就会导致内存泄露。

在 5.3 版本之后,做了这些优化:

  1. 并不是每次引用计数减少时都进入回收周期,只有根缓冲区满额后在开始垃圾回收;
  2. 可以解决循环引用问题;
  3. 可以总将内存泄露保持在一个阈值以下。

数据结构和算法

快速排序

通过设置一个初始中间值,来将需要排序的数组分成3部分,小于中间值的左边,中间值,大于中间值的右边,继续递归用相同的方式来排序左边和右边,最后合并数组

function quickSort($array)
{
    if(!isset($array[1]))
        return $array;
    $nMid = $array[0]; //获取一个用于分割的关键字,一般是首个元素
    $arrLeft = array();
    $arrRight = array();

    foreach($array as $v)
    {
        if($v > $nMid)
            $arrRight[] = $v;  //把比$nMid大的数放到一个数组里
        if($v < $nMid)
            $arrLeft[] = $v;   //把比$nMid小的数放到另一个数组里
    }

    $arrLeft   = quickSort($arrLeft); //把比较小的数组再一次进行分割
    $arrLeft[] = $nMid;        //把分割的元素加到小的数组后面,不能忘了它哦

    $arrRight = quickSort($arrRight);  //把比较大的数组再一次进行分割
    return array_merge($arrLeft,$arrRight);  //组合两个结果
    
}// END quickSort

冒泡排序

$arrArr = array(345,4,17,6,52,16,58,69,32,8,234);
$length = count($arrArr);
for ($i = 1; $i < $length; $i ++)
{
    for ($j = $length-1; $j >= $i; $j --)
    {
        if ($arrArr[$j] < $arrArr[$j-1])
        {
            $interim = $arrArr[$j-1];
            $arrArr[$j-1] = $arrArr[$j];
            $arrArr[$j] = $interim;
        }
    }
}

架构相关

S.O.L.I.D设计原则

  1. 单一功能原则
  2. 开闭原则
  3. 里氏替换原则
  4. 接口隔离原则
  5. 依赖反转原则

Laravel跟ThinkPHP的区别

  1. 加密方式不同,Laravel用的是Hash单向加密,ThinkPHP使用的Md5
  2. Laravel所有控制器的操作要基于路由,ThinkPHP不需要
  3. Laravel有中间件实现访问前后的处理,ThinkPHP没有中间件

参考

Laravel跟ThinkPHP的区别

ThinkPHP的生命周期

  1. 入口文件
  2. 引导文件
  3. 注册自动加载
  4. 注册错误和异常机制
  5. 应用初始化
  6. URL访问检测
  7. 路由检测
  8. 分发请求
  9. 响应输出
  10. 应用结束

参考

ThinkPHP生命周期

对MVC的理解

MVC是模型、视图、控制器的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面

优点

  1. 低耦合
  2. 重用性高
  3. 部署快
  4. 可维护性高

防护

  1. XSS跨站脚本攻击
  2. DDOS流量攻击
  3. CSRF跨站请求伪造攻击
  4. SQL注入

在前端表单用户输入进行控制或限制

由后端传参数和数据时进行过滤等等

参考

常见的各种攻击解决方案

项目

你如何做一个秒杀系统

列出四种解决方案,推荐去看一下前三种解决方案的例子。

  • 用缓存来处理抢购,避免直接操作数据库,使用 Redis 队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用
  • 库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false
  • 使用 Mysql 的事务,锁住操作的行
  • (不推荐使用)做一个延时处理,延时一段时间公布抢购结果

如何在项目中解决并发的问题

在前端控制有效请求,例如一分钟才正常请求一次

在后端过滤无效请求,将操作放进队列中实现,也可以用锁的机制,第二个等待第一个完成,一个接一个

参考

PHP高并发解决的思路

服务器

协议

Get 和 Post 的区别

Get 的参数包含在 URL,Get 请求会被浏览器主动缓存,有字符限制参数为 ASCII 字符

Post 通过 request body 传递参数,有多种编码方式

HTTP请求包含了什么

  1. 状态行
  2. 请求头
  3. 消息主体

HTTP状态码

分类 分类描述
1** 信息,服务器收到请求,需要请求着继续执行操作
2** 成功,操作被成功接受处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

三次握手

三次握手的目的是:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误

  1. 客户端发送一个包,来确保服务端能够接收到客户端传来的消息
  2. 服务端返回一个确认包,来表明可以接收到客户端传来的消息并能做出正确应答
  3. 客户端再次发送确认包,表明并可以做出正确应答

四次分手

  1. 主机A通知主机B没有数据要发送了
  2. 主机B收到主机A的信息返回一个报文段,告诉主机A同意关闭请求
  3. 主机B向主机A请求关闭连接,进入 LAST_ACK 状态
  4. 主机A收到并向主机B发送 ACK 报文段,主机B收到后关闭连接;然后主机A等待 2MSL 后依然没有收到回复,证明主机B已关闭,主机A关闭

URL执行过程

  1. DNS解析为IP地址
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染页面
  6. 连接结束

软件架构

RESTful架构是什么

是以 Web 为平台围绕着资源展开的建立 API 时遵守的一种规则 / 风格

RESTful 规定,数据的 CURD 操作对应着 HTTP 方法:

  • GET(SELECT):从服务器取出资源(一项或多项)
  • POST(CREATE):在服务器新建一个资源
  • PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)
  • PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)
  • DELETE(DELETE):从服务器删除资源

RESTful 的构成

https://api.example.com/v1/employees
  • 协议

    API 与用户的通信协议为 HTTPS 协议

  • 域名

    应该尽量将 API 部署在专用子域名下,或者独立域名

  • 版本

    应该将 API 的版本号放入 URL

  • 路径

    表明 API 的具体功能地址

  • HTTP 动词

    就是上方对应的 HTTP 方法

  • 过滤信息

    如果记录数量很多,服务器不可能将它们都返回给用户,API 应该提供参数,过滤返回结果,比如说指定返回多少条数据,或者页数之类的。

  • 状态码跟提示信息

  • 错误处理

  • 返回结果

    针对不同操作,返回结果符合规范

Redis

Redis类型有哪些

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

Mysql

基础

InnoDB跟MyISAM有什么区别

MyISAM性能强,执行速度快,支持表锁和全文索引

InnoDB安全性强,支持事务处理、外键跟行锁

参考

MyISAM跟InnoDB的区别

什么是索引

索引是一种数据结构,能帮助我们快速的检索数据库中的数据

索引有哪些,有什么特点

  • PRIMARY KEY 主键索引

    主键是一种唯一性索引,每个表只能有一个主键,在单表查询中,PRIMARY主键索引与UNIQUE唯一索引的检索效率并没有多大的区别,

    但在关联查询中,PRIMARY主键索引的检索速度要高于UNIQUE唯一索引。

  • INDEX 普通索引

    这是最基本的索引类型,而且它没有唯一性之类的限制。

  • UNIQUE 唯一索引

    这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。

  • FULLTEXT 全文索引

    MySql从3.23版开始支持全文索引和全文检索。全文索引只可以在VARCHAR或者TEXT类型的列上创建。

    对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。

  • 组合索引(较特殊)

    在索引的创建中,有两种场景,即为单列索引和多列索引

事务四大特性(ACID)

原子性、一致性、隔离性、持久性

参考

事务ACID原理

如何在项目中实现事务四大特性

原子性

利用 Innodb 的 undo log 回滚日志实现,当事务回滚时撤销所有已经成功执行的 sql 语句

一致性

从两个层面来保证一致性,从数据库层面来说,可以通过原子性、隔离性和持久性来保证一致性。

从应用层来说,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据

隔离性

利用锁机制来实现隔离性

持久性

利用 Innobd 的 redo log 重写日志

事务隔离级别

未提交读、已提交读、可重复读、序列化

参考

Innodb中的事务隔离级别和锁的关系

优化

初级优化

  1. 开启慢查询日志,监测SQL语句执行

    set global slow_query_log = ON;
    set global long_query_time = 3600;
    
  2. 优化不合理的SQL

  3. 在常用的字段上建立合理的索引

  4. 在联合索引查询中遵循最左原则

分库分表

参考

数据库分库分表思路

前端

如何解决跨域问题

PostMessage 跨域

使用 Iframe 标签定义需要跨域的域名,然后使用 contentWindow.postMessage 发送跨域数据,使用 window.addEventListener 接收返回数据

域名 A 下

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>       
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'aym'
        };
        // 向domain2传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
    };

    // 接受domain2返回数据
    window.addEventListener('message', function(e) {
        alert('data from domain2 ---> ' + e.data);
    }, false);
</script>

域名 B 下

// 接收domain1的数据
window.addEventListener('message', function(e) {
    alert('data from domain1 ---> ' + e.data);

    var data = JSON.parse(e.data);
    if (data) {
        data.number = 16;

        // 处理后再发回domain1
        window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
    }
}, false);
Jsonp 跨域
$.ajax({
    url: 'http://www.domain2.com:8080/login',
    type: 'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: "handleCallback",    // 自定义回调函数名
    data: {}
});

handleCallback(res)
{
    console.log(JSON.stringify(res));
}// END handleCallback

闭包

概念

闭包就是读取其他函数内部变量的函数

闭包的用途
  1. 读取其他函数内部的变量
  2. 让变量常驻内存

Other

小程序授权机制

  1. 首先小程序调用 wx.login 接口来获取到 code
  2. 拼接 AppId、Appsecret、code 使用curl调用微信接口获取到 openid 和 session_key
  3. 通过openid查询,如果用户是新用户就插入一条数据,返回新插入的主键ID,有的话直接返回ID
  4. 将ID、openid 生成一个字符串作为Token返回小程序

参考

微信小程序登录换取Token

小程序 Template 模版跟 Component 组件有什么区别

Template 模版是用来做显示作用的,只创建 wxml、wxss 文件就可以了

Component 组件有自己的业务逻辑,如果涉及到业务逻辑交互较多就用 Component

工作中的业务开发流程是怎样的

按自己实际理解讲