1. PHP的垃圾收集机制
PHP5.2及之前版本: PHP会根据引用计数refcount值来判断是不是垃圾,如果refcount值为0,PHP会当做垃圾释放掉,这种回收机制有缺陷,对于环状引用的变量无法回收。
PHP5.3及之后版本:
如果发现一个 zval 容器中的 refcount 在增加,说明不是垃圾;
如果发现一个 zval 容器中的 refcount 在减少,如果减到了0,直接当做垃圾回收;
如果发现一个 zval 容器中的 refcount 在减少,并没有减到0,PHP 会把该值放到缓冲区,当做有可能是垃圾的怀疑对象; 当缓冲区达到了临界值,PHP 会自动调用一个方法去遍历每一个值,如果发现是垃圾就清理。
2. php 数组底层实现原理
(1) 实现原理
底层实现是通过散列表(hash table) + 双向链表(解决hash冲突)
hashtable:将不同的关键字(key)通过映射函数计算得到散列值(Bucket->h) 从而直接索引到对应的Bucket
hash表保存当前循环的指针,所以foreach 比for更快(pInternalPointer)
-
Bucket:保存数组元素的key和value,以及散列值h
(2) 如何保证有序性
散列函数和元素数组(Bucket)中间添加一层大小和存储元素数组相同的映射表。
用于存储元素在实际存储数组中的下标
元素按照映射表的先后顺序插入实际存储数组中
映射表只是原理上的思路,实际上并不会有实际的映射表,而是初始化的时候分配Bucket内存的同时,还会分配相同数量的 uint32_t 大小的空间,然后将 arData 偏移到存储元素数组的位置。
(3) 解决hash重复(php使用的链表法)
链表法:不同关键字指向同一个单元时,使用链表保存关键字(遍历链表匹配key)
开放寻址法:当关键字指向已经存在数据的单元的时候,继续寻找其他单元,直到找到可用单元(占用其他单元位置,更容易出现hash冲突,性能下降)
(4) 基础知识
链表:队列、栈、双向链表、
链表 :元素 + 指向下一元素的指针
双向链表:指向上一元素的指针 + 元素 + 指向下一元素的指针
3. 常见的 PHP 安全性攻击
(1) Sql注入
使用mysql_real_escape_string()过滤数据 使用预处理语句并绑定变量 参数化SQL:是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值,用@或?来表示参数
(2) Xss
跨站点脚本攻击,由用户输入一些数据到你的网站,其中包括客户端脚本(通常JavaScript)。如果你没有过滤就输出数据到另一个web页面,这个脚本将被执行
防止:为了防止XSS攻击,使用PHP的htmlentities()函数过滤再输出到浏览器。
(3) Csrf
跨站点请求伪造,是指一个页面发出的请求,看起来就像是网站的信任用户,但是是伪造的
防止:一般来说,确保用户来自你的表单,并且匹配每一个你发送出去的表单。有两点一定要记住:对用户会话采用适当的安全措施,例如:给每一个会话更新id和用户使用SSL。生成另一个一次性的令牌并将其嵌入表单,保存在会话中(一个会话变量),在提交时检查它。如laravel中的 _token
4. Nginx与PHP的交互
- 用户先将域名或IP形式的http或https请求发送给部署好的nginx服务器,也就是客户端(这个可以是浏览器也可以是其他)和nginx服务器进行三次握手最终建立TCP连接的过程
- nginx服务器接收到用户访问的URI(协议加主机,不含端口)和后缀,服务器对该请求进行判断和处理
- 如果用户请求的是动态内容,nginx会将请求交给fastcgi客户端,通过fastcgi_pass将用户的请求发送给php-fpm,如果用户访问的是静态资源,那么nginx就不用做其他的工作了,将直接返回用户请求的静态资源返回给用户。当然静态请求地话用户和nginx服务器的交互流程就结束了,下面的是动态继续。
-
当php-fpm接收到CGI发送过来的请求时对接收到的消息进行封装(php-fpm.conf配置)到wrapper(FastCGI触发器),wrapper会产生一个新的线程调用php动态程序解析服务器(php.ini 这个是php的核心),执行php业务
即:Nginx -> FastCGI -> php-fpm -> FastCGI Wrapper -> php解析器
6.程序、进程、线程、 协程
程序
编译好的二进制文件,不占用资源。
进程
活跃着的程序,占用资源,是操作系统的基本单位。表示一个程序的上下文执行活动(打开、执行、保存…),是系统资源分配的最小单位,一个程序至少有一个进程。
线程
进程的执行单位,与进程共享资源。
一个进程至少有一个线程。进程执行程序时候的最小调度单位(执行a,执行b…),是CPU调度的最小单位.进程相当于一个容器,而线程而是运行在容器里面的,因此对于容器内的东西,线程是共同享有的,因此线程间的通信可以直接通过全局变量进行通信,共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入”互斥锁”。
协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。
迭代器最基本的规定了对象可以通过next返回下一个值,而不是像数组,列表一样一次性返回。
生成器: 使用 yield 关键字的函数,生成器也可通过next返回下一个值。
多线程比,协程有何优势?
极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显;
不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
7.swoole如何提升性能
1.进程常驻内存:
swoole本⾝是进程常驻内存,在进程启动的时候就将PHP框架等代码读取并编译完成,不需要每次启动的时候都执⾏编译步骤,⼤⼤降低了脚本的运⾏时间;
2.连接池
php-fpm的模式php因为每次请求结束时都会销毁所有资源,因此⽆法使⽤连接池;⽽基于swoole的进程常驻内存模式,可以通过连接池的⽅式来加速程序,使⽤连接池既可以降低程序的响应时间,⼜可以有效保护后端资源。
3.可以使⽤协程处理异步IO
当开发中需要去请求多处的数据,⽽每⼀块的数据单独请求都要花较长时间,常规的php-fpm是阻塞式运⾏,⽆法对这类型的数据处理进⾏加速;⽽基于swoole的程序,可以将这类的业务并⾏化处理,并⾏去请求后端的数据源,能够⼤⼤优化了此类业务的运⾏时间。
8. swoole⾥的协程是什么,怎么⽤?为什么协程可以提⾼并发?
协程是通过协作⽽不是抢占的⽅式来进⾏切换,它创建和切换对内存等资源⽐线程⼩的多(可以理解为更⼩的线程);
协程的使⽤是通过Swoole\Coroutine或者Co\命名空间短命名简化类名来创建;
协程可以异步处理任务,⽀持并发,并且资源消耗⼩。
9.⽤了swoole以后,会不会发⽣内存泄漏?如果发⽣了怎么解决?
swoole由于是常驻内存,⼀旦资源加载进⼊后,会⼀直存在于内存中。对于局部变量,swoole会在回调函数结束后⾃动释放;对于全局变量(global声明的变量,static声明的对象属性或者函数内的静态变量和超全局变量),swoole不会⾃动释放;因此操作不好会发⽣内存泄漏。
9. php数据结构
在PHP中数据结构共有9种,PHP有着非常强大的SPL标准库,其中提供了一套标准的数据结构,分别是双向链表,栈,队列,堆,最大堆,最小堆,优先列队,阵列,映射。
(1)双向链表:SplDoublyLinkedList
双链表是一种重要的线性存储结构,对于双链表中的每个节点,不仅仅存储自己的信息,还要保存前驱和后继节点的地址
(2)栈:SplStack
栈是一种特殊的线性表,因为它只能在线性表的一端进行插入或删除元素(即进栈和出栈,先入后出)
(3)队列:SplQueue
SplQueue 类通过使用一个双向链表来提供队列的主要功能。
(4)堆:SplHeap
堆(Heap)就是为了实现优先队列而设计的一种数据结构,它是通过构造二叉堆(二叉树的一种)实现。
(5)最大堆:SplMaxHeap
SplMaxHeap类提供堆的主要功能,将最大值保持在顶部。
最小堆:SplMinHeap
SplMinHeap类提供堆的主要功能,将最小值保持在顶部。
(6)优先列队:SplPriorityQueue
SplPriorityQueue是以堆数据结构来实现的,当我们出队时会拿出堆顶的元素,此时堆的特性被破坏,堆会进行相应的调整至稳定态(MaxHeap or MinHeap),即会将最后一个元素替换到堆顶,然后进行稳定态验证,不符合堆特性则继续调整,或者我们就得到了一个稳定态的堆,所以当优先级相同,出队顺序并不会按照入队顺序。
(7)阵列:SplFixedArray
SplFixedArray与普通的PHP Array不同,它是以数字为键名的固定长度的数组,它没有使用散列(Hash)存储方式,更接近于C语言的数组,因此效率更高。
(8)映射:SplObjectStorage
SplObjectStorage类实现了对象存储映射表,应用于需要唯一标识多个对象的存储场景。
10.echo(),print(),print_r()的区别?
Echo是PHP语句, print_r,print是函数,
Print()只能打印出简单类型变量的值(如int,string),有返回值。
print_r()可以打印出复杂类型变量的值(如数组,对象),有返回值
echo 输出一个或者多个字符串,无返回
13.使用微服务架构可以带来以下好处:
- 服务的独立部署:每个服务都是一个独立的项目,可以独立部署,不依赖于其他服务,耦合性低。
服务的快速启动:拆分之后服务启动的速度必然要比拆分之前快很多,因为依赖的库少了,代码量也少了。 - 更加适合敏捷开发:服务拆分可以快速发布新版本,修改哪个服务只需要发布对应的服务即可,不用整体重新发布。
- 职责专一,由专门的团队负责专门的服务:每个团队可以负责对应的业务线,服务的拆分有利于团队之间的分工。
- 服务可以动态按需扩容:当某个服务的访问量较大时,只需要将这个服务扩容即可。
- 代码的复用:每个服务都提供REST API,所有的基础服务都必须抽出来,很多的底层实现都可以以接口方式提供。
FastCGI协议运行原理
- FastCGI 进程管理器启动时会创建一个 主(Master) 进程和多个 CGI 解释器进程(Worker 进程),然后等待 Web 服务器的连接。
-Web 服务器接收 HTTP 请求后,将 CGI 报文通过 套接字(UNIX 或 TCP Socket)进行通信,将环境变量和请求数据写入标准输入,转发到 worker子进程。- worker进程完成处理后将标准输出和错误信息从同一连接返回给 Web 服务器。
- worker进程等待下一个 HTTP 请求的到来。
$_SERVER
$_SERVER['PHP_SELF']: 当前执行脚本的文件名。
$_SERVER['SERVER_NAME']: 当前运行脚本所在服务器的主机名。
$_SERVER['REQUEST_URI']: URL 的路径部分(不包括主机名和端口)。
$_SERVER['REQUEST_METHOD']: 请求使用的 HTTP 方法,如 "GET"、"POST" 等。
$_SERVER['HTTP_HOST']: 当前请求的主机头。
$_SERVER['HTTP_USER_AGENT']: 客户端发送的 User-Agent 头部信息,可以用来识别客户端类型。
$_SERVER['REMOTE_ADDR']: 客户端的 IP 地址。
$_SERVER['DOCUMENT_ROOT']: 当前运行脚本所在的文档根目录。
php7和php5区别
1.PHP7.0 比PHP5.6性能提升了两倍。
1). 变量存储字节减小,减少内存占用,提升变量操作速度
2). 改善数组结构,数组元素和hash映射表被分配在同一块内存里,降低了内存占用、提升了 cpu 缓存命中率
3). 改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率
2.PHP7.0全面一致支持64位。
3.PHP7.0之前出现的致命错误,都改成了抛出异常。
4.增加了空结合操作符(??)。效果相当于三元运算符。
5.PHP7.0新增了函数的返回类型声明。
6.PHP7.0新增了标量类型声明。
7.新增加了匿名类。
8.define 现在可以定义常量数组。
yield 是什么,说个使用场景
php中的yield关键字是在php 5.5版本引入的一个关键字,它在函数内部可以用来创建一个生成器(generator)。生成器可以用来遍历一个大数据集,而不需要一次性将整个数据集加载到内存中。
yield关键字的作用是将当前函数变成一个生成器函数。在生成器函数内部,我们可以使用yield语句将一个值返回给调用者,并且生成器函数的执行状态会被保存。下次调用生成器函数时,会从上次的yield语句处继续执行。
#文件、excel等的读取
function readTxt()
{
# code...
$handle = fopen("./test.txt", 'rb');
while (feof($handle)===false) {
# code...
yield fgets($handle);
}
fclose($handle);
}
foreach (readTxt() as $key => $value) {
# code...
echo $value.'<br/>';
}
HTTP Keep-Alive的作用
作用: Keep-Alive使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。Web服务器,基本上都支持HTTP Keep-Alive。
缺点: 对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,虽然为客户保留打开的连 接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时,Keep- Alive功能对资源利用的影响尤其突出。
解决:
http {
upstream backend {
server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300; //空闲连接数
keepalive_timeout 120s;//与上游空闲时间
keepalive_requests 100;//与上游请求处理最大次数
}
server {
listen 8080 default_server;
server_name "";
location / {
proxy_pass http://backend;
proxy_http_version 1.1; #设置http版本为1.1
proxy_set_header Connection ""; #设置Connection为长连接(默认为no)
}
}
#keepalive:限制nginx某个worker最多空闲连接数,此处不会限制worker与上游服务长连接的总数;
#keepalive_timeout:nginx与上游长连接最大空闲时间,默认值为60s;
#keepalive_requests:nginx与上游长连接最大交互请求的次数,默认值为100;
Apache与Nginx的优缺点比较
1、nginx相对于apache的优点:
轻量级,比apache 占用更少的内存及资源。高度模块化的设计,编写模块相对简单抗并发,nginx处理请求是异步非阻塞,多个连接(万级别)可以对应一个进程,而apache 则是阻塞型的,是同步多进程模型,一个连接对应一个进程,在高并发下nginx 能保持低资源低消耗高性能。nginx处理静态文件好,Nginx 静态处理性能比 Apache 高 3倍以上
2、apache 相对于nginx 的优点:
apache 的rewrite 比nginx 的rewrite 强大 ,模块非常多,基本想到的都可以找到 ,比较稳定,少bug ,nginx的bug相对较多
3:Nginx比Apache快的原因:这得益于Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型,而Apache则使用的是传统的select模型。
fastcgi、cgi、php-fpm
fastcgi和cgi的区别
在web服务器方面在对数据进行处理的进程方面:
a. cgi fork一个新的进程进行处理读取参数,处理数据,然后就结束生命期。
b. fastcgi 用tcp方式跟远程机子上的进程或本地进程建立连接要开启tcp端口,进入循环,等待数据的到来,处理数据。
php-fpm的作用
那PHP-FPM又是什么呢?它是一个实现了Fastcgi协议的程序,用来管理Fastcgi起的进程的,即能够调度php-cgi进程的程序。现已在PHP内核中就集成了PHP-FPM,使用--enalbe-fpm这个编译参数即可。另外,修改了php.ini配置文件后,没办法平滑重启,需要重启php-fpm才可。此时新fork的worker会用新的配置,已经存在的worker继续处理完手上的活
fastcgi_pass
Nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket.
- TCP是IP加端口,可以跨服务器.
- UNIX Domain Socket不经过网络,只能用于Nginx跟PHP-FPM都在同一服务器的场景.
用哪种取决于你的PHP-FPM配置:
#方式1:
php-fpm.conf: listen = 127.0.0.1:9000
nginx.conf: fastcgi_pass 127.0.0.1:9000;
#方式2:
php-fpm.conf: listen = /tmp/php-fpm.sock nginx.conf:
fastcgi_pass unix:/tmp/php-fpm.sock;
#其中php-fpm.sock是一个文件,由php-fpm生成,类型是srw-rw—-.
UNIX Domain Socket可用于两个没有亲缘关系的进程,是目前广泛使用的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的.这种通信方式是发生在系统内核里而不会在网络里传播.UNIX Domain Socket和长连接都能避免频繁创建TCP短连接而导致TIME_WAIT连接过多的问题.对于进程间通讯的两个程序,UNIX Domain Socket的流程不会走到TCP那层,直接以文件形式,以stream socket通讯.如果是TCP Socket,则需要走到IP层,对于非同一台服务器上,TCP Socket走的就更多了.
laravel框架的加载流程
Laravel的加载流程主要包括以下几个步骤:
- 注册应用程序的引导类和服务提供者。
- 加载环境配置文件。
- 配置应用程序的错误和日志处理。
- 注册类别别名。
- 注册服务容器绑定。
- 路由加载。
- 配置中间件。
- 注册视图合成器。
以下是Laravel框架的加载流程示例代码
//1注册应用程序的引导类和服务提供者
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\RouteServiceProvider::class);
// 2. 加载环境配置文件
$app->loadEnvironmentFrom('.env');
// 3. 配置应用程序的错误和日志处理
$app->configureMonologUsing(function($monolog) {
return $monolog->pushHandler(...);
});
// 4. 注册类别别名
class_alias('Illuminate\Support\Facades\Route', 'Route');
// 5. 注册服务容器绑定
$app->singleton('Example', function ($app) {
return new Example();
});
// 6. 路由加载
Route::get('/', function () {
return view('welcome');
});
// 7. 配置中间件
$app->middleware([
Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
...
]);
// 8. 注册视图合成器
View::composer('viewName', function ($view) {
$view->with('variable', $value);
});
// 9. 创建和启动应用程序
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
thinkphp框架加载过程
1,入口文件(tp5\public\index.php)
作用:
1)定义目录常量
2)加载框架引导目录
2.加载框架引导文件(tp5\thinkphp\start.php)
作用:
1)引导基础文件
2)对应用进行运转
3.加载框架基础引导文件(tp5\thinkphp\base.php)
作用:
1)加载系统常量
2)引入loader类(tp5\thinkphp\library\think\loader.php)
3)引入环境变量
4)注册自动记载机制
5)注册异常处理机制
6)记载管理配置(tp5\thinkphp\convention.php)
4.运行应用(tp5\thinkphp\library\think\App.php)下的run方法
第一步:加载当前控制器中的 initCommon()方法
第二步:加载当前控制器中的init()方法
1)加载各种配置文件
2)加载公共文件加载语言包
第三步:设置时区
第四步:加载当前控制器routeCheck()方法--路由检测
第五步:调用控制器中的exec方法
根据用户请求进行分发处理
第六步:根据不同请求类型加载对应文件module方法
加载到对应的控制器和对应的方法
5.响应输出
用户界面