前言
只适合工作(1-3)年的PHPer
写简历的网站:
超级简历 推荐
冷熊简历 刚开始我用的这个,后来感觉太过于简单,不过程序员也不需要什么花里胡哨的简历,如果不喜欢第一个可以用这个
PHP
基础
PHP生命周期
- 词法分析:将PHP文件转换成 Token
- 语法分析:根据生成的 Token 和语法规则进行分析
- Zend 引擎:将代码编译为 opcode 后并执行
- 调用 SAPI 输出函数返回执行结果
SAPI 是什么
SAPI 是服务器应用编程接口,作为应用层(比如 Apache、Nginx、CLI等)和 PHP 交互数据的入口
SAPI 实现了和各种应用层的兼容,应用层可以根据自身情况定制 SAPI,例:
- apache2handler 和 apache2filter,这是提供给 Apache mod_php 的 SAPI;
- cgi,Web Server fork 出 cgi 进程使用的 sapi;
- fastcgi,Web Server 采用网络通信或者网络 IPC 和 PHP 交换数据的 SAPI;
- cli,命令行方式运行 PHP 脚本的 SAPI
Session和Cookie的区别
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。
Coookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
参考
PHP7跟PHP5的区别,具体多了哪些新特性?
- 性能提升了两倍
- 增加了结合比较运算符和??运算符
- 增加了标量类型声明、返回类型声明(declare(strict_types=1):严格模式)
- try…catch增加了多条件判断,可以对更多的Error错误进行异常处理
- 增加了匿名类,可以直接使用new class来实例化一个匿名类
- define可定义常量数组
参考
魔术常量
__LINE__ 当前行号
__DIR__ 当前目录
__FILE__ 当前文件完整的文件地址
__FUNCTION__ 当前函数名
__METHOD__ 类的方法榠
__CLASS__ 类名
__NAMESPACE__ 当前命名空间名称
魔术方法
__construct、__destruct、__call、__callStatic、__get、__set、__isset、
__unset、__toString、__set_state、__debuginfo、__clone、__sleep、__wakeup、__invoke
参考
include 和 require 的区别
它们在加上
_ones后缀时表示已加载文件的不加载
-
加载失败的处理方式不同
require 加载失败,会报出一个 Fatal error 脚本停止执行
include 加载失败,会报出一个 Warning 脚本会继续执行
-
性能不同
include 执行文件每次都要进行读取和评估;
require 文件只处理一次
如果可能执行多次的代码,就用 require 效率比较高
简述一下 PHP 垃圾回收机制(GC)
PHP 5.3 版本之前都是采用引用计数的方式管理内存,PHP 所有的变量存在一个叫 zval 的变量容器中,当变量被引用的时候,引用计数会+1,变量引用计数变为0时,PHP 将在内存中销毁这个变量。
但是引用计数中的循环引用,引用计数不会消减为 0,就会导致内存泄露。
在 5.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设计原则
- 单一功能原则
- 开闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖反转原则
Laravel跟ThinkPHP的区别
- 加密方式不同,Laravel用的是Hash单向加密,ThinkPHP使用的Md5
- Laravel所有控制器的操作要基于路由,ThinkPHP不需要
- Laravel有中间件实现访问前后的处理,ThinkPHP没有中间件
参考
ThinkPHP的生命周期
- 入口文件
- 引导文件
- 注册自动加载
- 注册错误和异常机制
- 应用初始化
- URL访问检测
- 路由检测
- 分发请求
- 响应输出
- 应用结束
参考
对MVC的理解
MVC是模型、视图、控制器的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面
优点
- 低耦合
- 重用性高
- 部署快
- 可维护性高
防护
- XSS跨站脚本攻击
- DDOS流量攻击
- CSRF跨站请求伪造攻击
- SQL注入
在前端表单用户输入进行控制或限制
由后端传参数和数据时进行过滤等等
参考
项目
你如何做一个秒杀系统
列出四种解决方案,推荐去看一下前三种解决方案的例子。
- 用缓存来处理抢购,避免直接操作数据库,使用 Redis 队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用
- 库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false
- 使用 Mysql 的事务,锁住操作的行
- (不推荐使用)做一个延时处理,延时一段时间公布抢购结果
如何在项目中解决并发的问题
在前端控制有效请求,例如一分钟才正常请求一次
在后端过滤无效请求,将操作放进队列中实现,也可以用锁的机制,第二个等待第一个完成,一个接一个
参考
服务器
协议
Get 和 Post 的区别
Get 的参数包含在 URL,Get 请求会被浏览器主动缓存,有字符限制参数为 ASCII 字符
Post 通过 request body 传递参数,有多种编码方式
HTTP请求包含了什么
- 状态行
- 请求头
- 消息主体
HTTP状态码
| 分类 | 分类描述 |
|---|---|
| 1** | 信息,服务器收到请求,需要请求着继续执行操作 |
| 2** | 成功,操作被成功接受处理 |
| 3** | 重定向,需要进一步的操作以完成请求 |
| 4** | 客户端错误,请求包含语法错误或无法完成请求 |
| 5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
三次握手
三次握手的目的是:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
- 客户端发送一个包,来确保服务端能够接收到客户端传来的消息
- 服务端返回一个确认包,来表明可以接收到客户端传来的消息并能做出正确应答
- 客户端再次发送确认包,表明并可以做出正确应答
四次分手
- 主机A通知主机B没有数据要发送了
- 主机B收到主机A的信息返回一个报文段,告诉主机A同意关闭请求
- 主机B向主机A请求关闭连接,进入 LAST_ACK 状态
- 主机A收到并向主机B发送 ACK 报文段,主机B收到后关闭连接;然后主机A等待 2MSL 后依然没有收到回复,证明主机B已关闭,主机A关闭
URL执行过程
- DNS解析为IP地址
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
软件架构
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安全性强,支持事务处理、外键跟行锁
参考
什么是索引
索引是一种数据结构,能帮助我们快速的检索数据库中的数据
索引有哪些,有什么特点
-
PRIMARY KEY 主键索引
主键是一种唯一性索引,每个表只能有一个主键,在单表查询中,PRIMARY主键索引与UNIQUE唯一索引的检索效率并没有多大的区别,
但在关联查询中,PRIMARY主键索引的检索速度要高于UNIQUE唯一索引。
-
INDEX 普通索引
这是最基本的索引类型,而且它没有唯一性之类的限制。
-
UNIQUE 唯一索引
这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。
-
FULLTEXT 全文索引
MySql从3.23版开始支持全文索引和全文检索。全文索引只可以在VARCHAR或者TEXT类型的列上创建。
对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。
-
组合索引(较特殊)
在索引的创建中,有两种场景,即为单列索引和多列索引
事务四大特性(ACID)
原子性、一致性、隔离性、持久性
参考
如何在项目中实现事务四大特性
原子性
利用 Innodb 的 undo log 回滚日志实现,当事务回滚时撤销所有已经成功执行的 sql 语句
一致性
从两个层面来保证一致性,从数据库层面来说,可以通过原子性、隔离性和持久性来保证一致性。
从应用层来说,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据
隔离性
利用锁机制来实现隔离性
持久性
利用 Innobd 的 redo log 重写日志
事务隔离级别
未提交读、已提交读、可重复读、序列化
参考
优化
初级优化
-
开启慢查询日志,监测SQL语句执行
set global slow_query_log = ON; set global long_query_time = 3600; -
优化不合理的SQL
-
在常用的字段上建立合理的索引
-
在联合索引查询中遵循最左原则
分库分表
参考
前端
如何解决跨域问题
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
闭包
概念
闭包就是读取其他函数内部变量的函数
闭包的用途
- 读取其他函数内部的变量
- 让变量常驻内存
Other
小程序授权机制
- 首先小程序调用
wx.login接口来获取到code - 拼接
AppId、Appsecret、code使用curl调用微信接口获取到 openid 和 session_key - 通过openid查询,如果用户是新用户就插入一条数据,返回新插入的主键ID,有的话直接返回ID
- 将ID、openid 生成一个字符串作为Token返回小程序
参考
小程序 Template 模版跟 Component 组件有什么区别
Template 模版是用来做显示作用的,只创建 wxml、wxss 文件就可以了
Component 组件有自己的业务逻辑,如果涉及到业务逻辑交互较多就用 Component
工作中的业务开发流程是怎样的
按自己实际理解讲