php 面试题-精简版-适用于1年以上经验

php 面试题-精简版

以前的面试题太多了。几百道。现在整理几个精华吧。
laravel 不在这里哦。

资料均来源于互联网。

表单提交中的Get和Post的异同点

get 请求一般用于向服务端获取数据,post 一般向服务端提交数据
get 传输的参数在 url 中,传递参数大小有限制,post 没有大小限制,
get 不安全,post 安全性比get高
get请求在服务端用Request.queryString 接受 ,post 请求在服务端用Requset.form 接受

浅谈HTTP中Get、Post、Put与Delete的区别

1、GET请求会向数据库发索取数据的请求,从而来获取信息,该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改、增加数据,不会影响资源的内容,即该请求不会产生副作用。无论进行多少次操作,结果都是一样的。

2、与GET不同的是,PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。

3、POST请求同PUT请求类似,都是向服务器端发送数据的,但是该请求会改变数据的种类等资源,就像数据库的insert操作一样,会创建新的内容。几乎目前所有的提交操作都是用POST请求的。

4、DELETE请求顾名思义,就是用来删除某一个资源的,该请求就像数据库的delete操作。

就像前面所讲的一样,既然PUT和POST操作都是向服务器端发送数据的,那么两者有什么区别呢。。。POST主要作用在一个集合资源之上的(url),而PUT主要作用在一个具体资源之上的(url/xxx),通俗一下讲就是,如URL可以在客户端确定,那么可使用PUT,否则用POST。

综上所述,我们可理解为以下:

1、POST /url 创建
2、DELETE /url/xxx 删除
3、PUT /url/xxx 更新
4、GET /url/xxx 查看

echo print() print_r() var_dump()的区别

常见的输出语句

echo()
可以一次输出多个值,多个值之间用逗号分隔。echo是语言结构(language construct),而并不是真正的函数,因此不能作为表达式的一部分使用。

print()
函数print()打印一个值(它的参数),如果字符串成功显示则返回true,否则返回false。

print_r()
可以把字符串和数字简单地打印出来,而数组则以括起来的键和值得列表形式显示,并以Array开头。但print_r()输出布尔值和NULL的结果没有意义,因为都是打印”\n”。因此用var_dump()函数更适合调试。

var_dump()
判断一个变量的类型与长度,并输出变量的数值,如果变量有值输的是变量的值并回返数据类型。此函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

编写一段用最小代价实现将字符串完全反序. 将 “1234567890” 转换成 “0987654321”. (用前述你最熟悉的语言编写并标注简单注释, 不要使用函数)

public function ceshi(){
       $a = '1234567890';
       $i = 0;
       $b = '';
       while (isset($a[$i]) && $a[$i] != null){
           $b = $a[$i++].$b;
       }

       echo $b;
    }

www.cnblogs.com/xiong-hua/p/138940...

补充,函数: PHP strrev() 函数

www.w3school.com.cn/php/func_strin...

请用递归实现一个阶乘求值算法 F(n): n=5;F(n)=5!=54321=120

function F($n) {
    if($n==0) {         
        return 1;      
    }else{         
        return $n* F($n-1);      
    }
}
var_dump(F(5));

将字符长pai-huang-pian 转化为驼峰法的形式:PaiHuangPian

方法一

function Fun($str){   
     if(isset($str) && !empty($str)){       
        $newStr='';       
        if(strpos($str,'-')>0){          
            $strArray=explode('-',$str);          
            $len=count($strArray);           
            for ($i=0;$i<$len;$i++){               
            $newStr.=ucfirst($strArray[$i]);           
            } 
         }      
     return $newStr;    
     }
 }

方法二

function Fun($str){  
     $arr1=explode('_',$str);   
     $str = implode(' ',$arr1);   
     return ucwords($str);
 }
 var_dump(Fun("fang-zhi-gang")); //FangZhiGang

数组内置的排序方法有哪些?

sort($array); //数组升序排序
rsort($array); //数组降序排序

asort($array);  //根据值,以升序对关联数组进行排序
ksort($array);  //根据建,以升序对关联数组进行排序

arsort($array);   //根据值,以降序对关联数组进行排序
krsort($array);  // 根据键,以降序对关联数组进行排序

可参考:www.runoob.com/php/php-arrays-sort...

语句include和require的区别是什么?为避免多次包含同一文件,可用什么语句代替它们?

【这题,老兄弟连人都知道哈哈哈】

答1:

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

include在引入不存在的文件时,产生一个警告且脚本还会继续执行,
require则会导致一个致命性错误且脚本停止执行。

2、include()是有条件包含函数,而 require()则是无条件包含函数。

if(FALSE){
include 'file.php'; //file.php不会被引入
}

if(FALSE){
require 'file.php'; //file.php将会被引入

3、文件引用方式

include有返回值,而require没有

$retVal = include(’somefile.php’);

if(!empty($retVal)){
    echo "文件包含成功";
}else{
    echo "文件包含失败";
}

答2: 可以用include_oncerequire_once代替,表示文件只引入一次,引入之后则不在引入,作为优化点

PHP 不使用第三个变量实现交换两个变量的值 ***

这题,我经常遇到,也不知道为啥。标记一下,三个星号

方法一str_replace

<?php
/**
 * 双方变量为字符串时,可用交换方法二
 * 使用str_replace()方法达到交换变量值得目的
 * 此方法较第一种,逻辑上稍微简单点
 */
$a = "This is A"; // a变量原始值
$b = "This is B"; // b变量原始值
echo '交换之前 $a 的值:'.$a.', $b 的值:'.$b,'<br>'; // 输出原始值
$a .= $b; // 将$b的值追加到$a中
$b = str_replace($b, "", $a); // 在$a(原始$a+$b)中,将$b替换为空,则余下的返回值为$a
$a = str_replace($b, "", $a); // 此时,$b为原始$a值,则在$a(原始$a+$b)中将$b(原始$a)替换为空,则余下的返回值则为原始$b,交换成功
echo '交换之后 $a 的值:'.$a.', $b 的值:'.$b,'<br>'; // 输出结果值

方法二 list 函数

list($b,$a)=array($a,$b);
var_dump($a,$b);

www.w3school.com.cn/php/func_array...

一个文件的路径为 /wwwroot/include/page.class.php,写出获得该文件扩展名的方法

$arr = pathinfo(/wwwroot/include/page.class.php”);

$str = substr($arr[‘basename’],strrpos($arr[‘basename’],.));

博客:[面试题]跳槽面试必背-自己最近5年的整理,欢迎大家补充。 第108道题

简述 php 的垃圾收集机制。

php中的变量存储在变量容器zval中,zval中除了存储变量类型和值外,还有is_ref和refcount字段。refcount表示指向变量的元素个数,is_ref表示变量是否有别名。如果refcount为0时,就回收该变量容器。如果一个zval的refcount减1之后大于0,它就会进入垃圾缓冲区。当缓冲区达到最大值后,回收算法会循环遍历zval,判断其是否为垃圾,并进行释放处理。

PHP 如何接口调用?

使用curl调用http接口:
get

<?php
     //初始化
     $curl  =  curl_init ( ) ;
     //设置抓取的url
     curl_setopt ( $curl , CURLOPT_URL ,  'http://www.baidu.com' ) ;
     //设置头文件的信息作为数据流输出
     curl_setopt ( $curl , CURLOPT_HEADER ,  1 ) ;
     //设置获取的信息以文件流的形式返回,而不是直接输出。
     curl_setopt ( $curl , CURLOPT_RETURNTRANSFER ,  1 ) ;
     //执行命令
     $data  =  curl_exec ( $curl ) ;
     //关闭URL请求
     curl_close ( $curl ) ;
     //显示获得的数据
     print_r ( $data ) ;
?>

post

<?php
     //初始化
     $curl  =  curl_init ( ) ;
     //设置抓取的url
     curl_setopt ( $curl , CURLOPT_URL ,  'http://www.baidu.com' ) ;
     //设置头文件的信息作为数据流输出
     curl_setopt ( $curl , CURLOPT_HEADER ,  1 ) ;
     //设置获取的信息以文件流的形式返回,而不是直接输出。
     curl_setopt ( $curl , CURLOPT_RETURNTRANSFER ,  1 ) ;
     //设置post方式提交
     curl_setopt ( $curl , CURLOPT_POST ,  1 ) ;
     //设置post数据
     $post_data  =  array (
         "username"  =>  "coder" ,
         "password"  =>  "12345"
         ) ;
     curl_setopt ( $curl , CURLOPT_POSTFIELDS ,  $post_data ) ;
     //执行命令
     $data  =  curl_exec ( $curl ) ;
     //关闭URL请求
     curl_close ( $curl ) ;
     //显示获得的数据
     print_r ( $data ) ;
?>

php 命名空间是如何定义的?#

命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间,除了一个以外:declare关键字。
命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误。这种情况下只要避免命名重复就可以解决,最常见的一种做法是约定一个前缀。
基础
命名空间将代码划分出不同的空间(区域),每个空间的常量、函数、类(为了偷懒,我下边都将它们称为元素)的名字互不影响, 这个有点类似我们常常提到的‘封装'的概念

php 接口是如何实现的?

如果一个抽象类里面的所有方法都是抽象方法,且没有声明变量,而且接口里面所有的成员都是public权限的,那么这种特殊的抽象类就叫接口
接口是什么?
使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
接口使用规范
接口不能实例化
接口的属性必须是常量
接口的方法必须是public【默认public】,且不能有函数体
类必须实现接口的所有方法
一个类可以同时实现多个接口,用逗号隔开
接口可以继承接口【用的少】

什么是 Composer, 工作原理是什么?

Composer 是 PHP 的一个依赖管理工具。工作原理就是将已开发好的扩展包从 packagist.org composer 仓库下载到我们的应用程序中,并声明依赖关系和版本控制。

composer团队协作怎么保证版本统一?

安装组件使用composer install 而不是composer update,
.lock文件加入版本控制当中。

OOP思想,特征和其意义

抽象、封装、继承和多态是面向对象的基础。

抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。

封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)

继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。

多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。

帮助理解: www.cnblogs.com/waj6511988/p/69742...

设计模式之 SOLID 原则

php 设计模式不多说了,但是记住,要能讲出来.

SRP 单一责任原则
OCP 开放封闭原则
LSP 里氏替换原则
ISP 接口隔离原则
DIP 依赖倒置原则

  • 开闭原则:对扩展开放,对修改关闭
  • 里氏替换原则:继承 必须保证 父类中的性质在子类中仍然成立
  • 依赖倒置原则:面向接口编程,而不面向实现类
  • 单一职责原则:控制 类的 粒度的大小 ,增强内聚性,减少耦合
  • 接口隔离原则:要为各个类提供所需的专用接口
  • 迪米特法则:迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD。
  • 合成复用原则:尽可能使用组合或者聚合等关系来关联类,其次才考虑使用继承。

mvc框架的生命周期说一下

model:存放向数据库请求来的数据
view:存放组件、图片、页面模板html文件
controller:获取或改变model里的数据返回给页面渲染数据
用户请求进来,先加载配置文件,框架初始化,然后匹配路由地址,寻找到对应的controller的文件地址,引入加载文件,实例化controller,根据路由匹配得到的方法和参数,调用并传参到方法,此处可能需要读取db,model层则负责数据库存取,提供封装好的方法给到controller层调用,controller层得到数据后,通过引入view层文件,传递数据到view层,渲染html模板后输出。

可以参考:www.jb51.net/article/49498.htm

session与cookie的区别是什么?

1、保持状态:

cookie和session都是用来跟踪浏览器用户身份的会话方式。
2、使用方式:
(1)cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。

Cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它

(2)session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。

3、存储内容:

cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)

4、存储的大小:

cookie:单个cookie保存的数据不能超过4kb;

session大小没有限制。

5、安全性:

cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;
session的安全性大于cookie。

为什么session的安全性大于cookie?

sessionID存储在cookie中,若要攻破session首先要攻破cookie;
sessionID是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionID;
第二次启动session_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。
sessionID是加密的
综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。

session与cookie的应用场景有哪些?

cookie:
(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(2)保存上次登录的时间等信息。
(3)保存上次查看的页面。
(4)浏览计数。

session:

Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。

(1)网上商城中的购物车。
(2)保存用户登录信息。
(3)将某些数据放入session中,供同一用户的不同页面使用。
(4)防止用户非法登录。

存放 session 的三种方法

1、如果你能修改到服务器配置文件,那就打开打开php.ini
修改下面两项:

session.save_handler = memcache
session.save_path = "tcp://127.0.0.1:11211"

2、修改网站根目录下的.htaccess文件

php_value session.save_handler "memcache"
php_value session.save_path  "tcp://127.0.0.1:11211"

3、在程序代码中修改

ini_set("session.save_handler", "memcache");
ini_set("session.save_path", "tcp://127.0.0.1:11211");

IoC 容器是什么?

IoC(Inversion of Control)译为 「控制反转」,也被叫做「依赖注入」(DI)。什么是「控制反转」?对象 A 功能依赖于对象 B,但是控制权由对象 A 来控制,控制权被颠倒,所以叫做「控制反转」,而「依赖注入」是实现 IoC 的方法,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中。

其作用简单来讲就是利用依赖关系注入的方式,把复杂的应用程序分解为互相合作的对象,从而降低解决问题的复杂度,实现应用程序代码的低耦合、高扩展。

Laravel 中的服务容器是用于管理类的依赖和执行依赖注入的工具。

Facades 是什么?

Facades(一种设计模式,通常翻译为外观模式)提供了一个”static”(静态)接口去访问注册到 IoC 容器中的类。提供了简单、易记的语法,而无需记住必须手动注入或配置的长长的类名。此外,由于对 PHP 动态方法的独特用法,也使测试起来非常容易。

Contract 是什么?

Contract(契约)是 laravel 定义框架提供的核心服务的接口。Contract 和 Facades 并没有本质意义上的区别,其作用就是使接口低耦合、更简单。

依赖注入的原理?

依赖注入 (DI) 和控制反转 (IOC) 是从不同的角度的描述的同一件事情,就是指通过引入 IOC 容器,利用依赖关系注入的方式,实现对象之间的解耦。

我们把依赖注入应用到软件系统中,再来描述一下这个过程:
对象 A 依赖于对象 B, 当对象 A 需要用到对象 B 的时候,IOC 容器就会立即创建一个对象 B 送给对象 A。IOC 容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由 IOC 容器包办。
在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用 new 关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC 很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。

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

性能提升了两倍
结合比较运算符 (<=>)
标量类型声明
返回类型声明
try…catch 增加多条件判断,更多 Error 错误可以进行异常处理
匿名类,现在支持通过 new class 来实例化一个匿名类,这可以用来替代一些 “用后即焚” 的完整类定义
…… 了解更多查看文章链接 PHP7 新特性

为什么 PHP7 比 PHP5 性能提升了?

变量存储字节减小,减少内存占用,提升变量操作速度
改善数组结构,数组元素和 hash 映射表被分配在同一块内存里,降低了内存占用、提升了 cpu 缓存命中率
改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率

php7新特性

标量类型声明
返回值类型声明
语法糖:null合并运算符,太空船操作符
define允许定义常量数组,
匿名类,
新增了一些函数intdiv(),随机函数,

1、php7.0相比于php5.6的新特性

参考:php.net/manual/zh/migration70.new-f...

2、php7.1相对于php7.0的新特性

参考:php.net/manual/zh/migration71.new-f...

3、php7.2相对于php7.1的新特性

参考:php.net/manual/zh/migration72.new-f...

php8新特性

新增联合类型(Union Types);
添加了 WeakMap;
添加了 ValueError 类;
新增的特性大多是语法糖,主要是JIT。

JIT是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行,在PHP8中,Zend VM不需要解释某些操作码,并且这些指令将直接作为CPU级指令执行。

IT和opcache区别

要说明opcode cache与JIT的区别,得先明白,字节码,又叫中间码与机器码的区别。

简答的说,提升php执行效率,更快了。

相关链接 www.laruence.com/2020/06/27/5963.h...

简述 php 的垃圾收集机制。

关键词:使用了引用计数器

简述:

1.  PHP可以自动进行内存管理,清除不需要的对象,主要使用了引用计数。
2. php中的变量存储在变量容器zval中,zval中除了存储变量类型和值外,还有is_ref和refcount字段。
refcount表示指向变量的元素个数,is_ref表示变量是否有别名。
如果refcount为0时,就回收该变量容器。
3. 为了解决循环引用内存泄露问题 , 使用同步周期回收算法。
如果一个zval的refcount减1之后大于0,它就会进入垃圾缓冲区。
当缓冲区达到最大值后,回收算法会循环遍历zval,判断其是否为垃圾,并进行释放处理。

www.php.net/manual/zh/features.gc....

php-fpm是什么?

PHP5.3.3开始集成了php-fpm模块,不再是第三方的包了。PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置。

重点:

php-fpm是fastcgi的实现。

PHP-FPM(FastCGI Process Manager:FastCGI进程管理器)是一个PHPFastCGI管理器,对于PHP 5.3.3之前的php来说,是一个补丁包 [1]  ,旨在将FastCGI进程管理整合进PHP包中。如果你使用的是PHP5.3.3之前的PHP的话,就必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。
相对Spawn-FCGIPHP-FPMCPU和内存方面的控制都更胜一筹,而且前者很容易崩溃,必须用crontab进行监控,而PHP-FPM则没有这种烦恼。

php-fpm是 FastCGI 的实现,并提供了进程管理的功能。 
进程包含 master 进程和 worker 进程两种进程。 
master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

使用PHP-FPM来控制PHP-CGI的FastCGI进程
/usr/local/php/sbin/php-fpm{start|stop|quit|restart|reload|logrotate}
--start 启动php的fastcgi进程
--stop 强制终止php的fastcgi进程
--quit 平滑终止php的fastcgi进程
--restart 重启php的fastcgi进程
--reload 重新平滑加载php的php.ini
--logrotate 重新启用log文件

php-fpm的运行模型?

多进程同步阻塞模式

php-fpm是一种master(主)/worker(子)多进程架构模型。

当PHP-FPM启动时,会读取配置文件,然后创建一个Master进程和若干个Worker进程(具体是几个Worker进程是由php-fpm.conf中配置的个数决定)。Worker进程是由Master进程fork出来的。

master进程主要负责CGI及PHP环境初始化、事件监听、Worker进程状态等等,worker进程负责处理php请求。

master进程负责创建和管理woker进程,同时负责监听listen连接,master进程是多路复用的;woker进程负责accept请求连接,同时处理请求,一个woker进程可以处理多个请求(复用,不需要每次都创建销毁woker进程,而是达到处理一定请求数后销毁重新fork创建worker进程),但一个woker进程一次只能处理一个请求。

cgi,php-cgi,php-fpm,fastcgi的区别?

cgi
cgi是一个web server与cgi程序(这里可以理解为是php解释器)之间进行数据传输的协议,保证了传递的是标准数据。

php-cgi
php-cgi是php解释器。他自己本身只能解析请求,返回结果,不会管理进程。php-fpm是调度管理php-cgi进程的程序。

Fastcgi
Fastcgi是用来提高cgi程序(php-cgi)性能的方案/协议。

cgi程序的性能问题在哪呢?”PHP解析器会解析php.ini文件,初始化执行环境”,就是这里了。标准的CGI对每个请求都会执行这些步骤,所以处理的时间会比较长。

Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复劳动,效率自然提高。而且当worker不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是Fastcgi的对进程的管理。

php-fpm
fastcgi是一个方案或者协议,php-fpm就是FastCGI的后端实现,也就是说,进程分配和管理是FPM来做的。官方对FPM的解释:【Fastcgi Process Manager】【Fastcgi 进程管理器】。

php-fpm的管理对象是php-cgi,他负责管理一个进程池,来处理来自Web服务器的请求。

对于php.ini文件的修改,php-cgi进程是没办法平滑重启的,有了php-fpm后,就把平滑重启成为了一种可能,php-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度的。

php-fpm如何完成平滑重启?

修改php.ini之后,php-cgi进程的确是没办法平滑重启的。php-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度。

php-fpm和nginx的通信机制是怎么样的?

看下nginx的配置文件:
Nginx中fastcgi_pass的配置:

location ~ .php$ {  
root /home/wwwroot;  
fastcgi\_pass 127.0.0.1:9000;
    #fastcgi_pass  unix:/var/run/php-fpm/php-fpm.sock;
    #fastcgi_pass  unix:/tmp/php-cgi.sock;
    try_files $uri /index.php =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

因为nginx不能直接执行php,所以需要借用fastcgi模块,和php-fpm进行通信。有两种方式;

TCP
UNIX Domain Socket
TCP是IP加端口,可以跨服务器;

而UNIX Domain Socket不经过网络,只能用于Nginx跟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;

值得一提的是,MySQ命令行客户端连接mysqld服务也类似有这两种方式:
使用Unix Socket连接(默认):

mysql -uroot -p --protocol=socket --socket=/tmp/mysql.sock

使用TCP连接:

mysql -uroot -p --protocol=tcp --host=127.0.0.1 --port=3306

怎么选定用tcp还是套接字的方式和nginx通信?
tcp方式是面向链接的协议,更稳定。
套接字效率更高,但是限制nginx和php-fpm都在一台服务器。

php-fpm在请求链路的体现,画出来?

www.example.com
       |
       |
      \!/
     Nginx
       |
       |
      \!/
路由到www.example.com/index.php
       |
       |
      \!/
加载nginx的fast-cgi模块
       |
       |
      \!/
fast-cgi监听127.0.0.1:9000地址
       |
       |
      \!/
www.example.com/index.php请求到达127.0.0.1:9000
       |
       |
      \!/
php-fpm 监听127.0.0.1:9000
       |
       |
      \!/
php-fpm 接收到请求,启用worker进程处理请求
       |
       |
      \!/
php-fpm 处理完请求,返回给nginx
       |
       |
      \!/
nginx将结果通过http返回给浏览器

php-fpm有几种工作模式?

PHP-FPM进程管理方式有动态(Dynamic)、静态(Static)、按需分配(Ondemand)三种。

动态

会初始化创建一部分worker,在运行过程中,动态调整worker数量,最大worker数受pm.max_children和process.max

listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 6

当空闲进程数小于min_spare_servers时,创建新的子进程,总子进程数小于等于pm.max_children,小于等于process.max
当空闲进程数大于max_spare_servers,会杀死启动时间最长的子进程
如果子进程(idle状态)数大于max_children,会打印warning日志,结束处理
process小于 max_children ,计算一个num,启动num个worker
优点:动态扩容,不浪费系统资源
缺点:所有worker都在工作,新的请求到来需要等待创建worker进程,最长等待1s(内部存在一个1s的定时器,去查看,创建进程),频繁启停进程消耗cpu,请求数稳定,不需要频繁销毁
静态

启动固定大小数量的worker,也有1s的定时器,用于统计进程的一些状态信息,例如空闲worker个数,活动worker个数

pm.max_children = 10 #必须配置这个参数,而且只有这个参数有效

优点:不用动态判断负载,提升性能
缺点:如果配置成static,只需要考虑max_children数量,数量取决于cpu的个数和应用的响应时间,一次启动固定大小进程浪费系统资源
按需分配

php-fpm启动的时候不会启动worker进程,按需启动worker,有链接进来后,才会启动

listen = 127.0.0.1:9001
pm = ondemand 
pm.process_idle_timeout = 60
pm.max_children = 10

连接到来时(只有链接,不没有数据也会创建,telnet也会创建),创建新worker进程,worker进程数的创建收max_children设置限制,也受限于全局的process.max设置(三种模式都受限此,下文中有全局配置项讲解),如果空闲时间超过了process_idle_timeout的设置就会销毁worker进程

优点:按流量需求创建,不浪费系统资源,
缺点:因为php-fpm是短连接的,如果每次请求都先建立连接,大流量场景下会使得master进程变得繁忙,浪费cpu,不适合大流量模式

不推荐使用此模式

工作模式 特点
动态 均衡优先,适合小内存服务器,2g左右
静态 性能优先, 适合大内存机器
按需分配 内存优先,适合微小的内存,2g以下

怎么选定php-fpm的worker进程数?

动态建立进程个数

  • N+20% 到 M/m之间
  • N是cpu核数,M是内存,m是每个php进程内存数

静态进程个数

  • M/(m*1.2)
  • pm.max_requests, 设置最大请求数,达到这个数量以后,会自动长期worker进程,繁殖内存意外增长
    注意:PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M。所以需要每个worker进程处理完一定的请求后,销毁重新创建。

cpu密集型的pm.max_children不能超过cpu内核数,但是web服务属于IO密集型的,可以将pm.max_children的值设置大于cpu核数。

php-fpm如何优化?

(1)避免程序跑死(hang)

在负载较高的服务器上定时重载php-fpm,reload可以平滑重启而不影响生产系统的php脚本运行,每15分钟reload一次,定时任务如下:

0-59/15 * * * * /usr/local/php/sbin/php-fpm reload

(2)合理增加单个worker进程最大处理请求数,减少内存消耗

最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn新的。该配置可以避免php解释器自身或程序引起的memory leaks。默认值是500,可以修改为如下配置:

pm.max_requests = 1024
(3)开启静态模式,指定数量的php-fpm进程,减少内存消耗

说下你最常用的php框架(laravel框架)的生命周期?

自己在站内搜。

生命周期概览

第一件事

Laravel 应用的所有请求入口都是 public/index.php 文件,所有请求都会被 web 服务器(Apache/Nginx)导向这个文件。 index.php 文件包含的代码并不多,但是,这里是加载框架其它部分的起点。

index.php 文件载入 Composer 生成的自动加载设置,然后从 bootstrap/app.php 脚本获取 Laravel 应用实例,Laravel 的第一个动作就是创建服务容器实例。

HTTP/Console 内核

接下来,请求被发送到 HTTP 内核或 Console 内核(分别用于处理 Web 请求和 Artisan 命令),这取决于进入应用的请求类型。这两个内核是所有请求都要经过的中央处理器,现在,就让我们聚焦在位于 app/Http/Kernel.php 的 HTTP 内核。

HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类,该类定义了一个 bootstrappers 数组,这个数组中的类在请求被执行前运行,这些 bootstrappers 配置了错误处理、日志、检测应用环境以及其它在请求被处理前需要执行的任务。

HTTP 内核还定义了一系列所有请求在处理前需要经过的 HTTP 中间件,这些中间件处理 HTTP 会话的读写、判断应用是否处于维护模式、验证 CSRF 令牌等等。

HTTP 内核的 handle 方法签名相当简单:获取一个 Request,返回一个 Response,可以把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应。

服务提供者

内核启动过程中最重要的动作之一就是为应用载入服务提供者,应用的所有服务提供者都被配置在 config/app.php 配置文件的 providers 数组中。首先,所有提供者的 register 方法被调用,然后,所有提供者被注册之后,boot 方法被调用。

服务提供者负责启动框架的所有各种各样的组件,比如数据库、队列、验证器,以及路由组件等,正是因为他们启动并配置了框架提供的所有特性,所以服务提供者是整个 Laravel 启动过程中最重要的部分。

分发请求

一旦应用被启动并且所有的服务提供者被注册,Request 将会被交给路由器进行分发,路由器将会分发请求到路由或控制器,同时运行所有路由指定的中间件。

聚焦服务提供者

服务提供者是启动 Laravel 应用中最关键的部分,应用实例被创建后,服务提供者被注册,请求被交给启动后的应用进行处理,整个过程就是这么简单!

对 Laravel 应用如何通过服务提供者构建和启动有一个牢固的掌握非常有价值,当然,应用默认的服务提供者存放在 app/Providers 目录下。

默认情况下,AppServiceProvider 是空的,这里是添加自定义启动和服务容器绑定的最佳位置,当然,对大型应用,你可能希望创建多个服务提供者,每一个都有着更加细粒度的启动。

laravel请求生命周期流程图,如下
php 面试题-精简版-适用于1年以上经验

什么是依赖注入,解决了那些问题

1.什么是依赖注入
    依赖注入是控制反转的一种实现,实现代码解耦,便于单元测试。因为它并不需要了解自身所依赖的类,而只需要知道所依赖的类实现了自身所需要的方法就可以了。

2.解决那些问题
    依赖之间的解耦
    单元测试,方便Mock

laravel-china.org/topics/2104/unde…

什么是控制反转

控制反转 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection, DI, 还有一种叫"依赖查找"(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

怎么理解 依赖注入(DI)与控制反转(Ioc)?

这让我想起了怎么理解nginx正向代理和反向代理…

IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理;
DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入;

依赖注入和控制反转说的实际上是同一个东西,它们是一种设计模式,这种设计模式用来减少程序间的耦合。依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。

1、依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部资源;

2、而控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

laravel的控制反转

是通过反射和递归实现的容器,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖,使类之间耦合度更低。

php的弱类型是怎么实现的?

php是通过c语言进行实现,但是c语言为强类型,那php的弱语言类型是通过PHP底层设计了一个zval(“Zend value”的缩写)的数据结构,可以用来表示任意类型的PHP值。通过共同体实现弱类型变量声明。

简单说下对php 底层变量(zval)数据结构的理解

1. 变量存储结构

变量的值存储到以下所示zval结构体中。 zval结构体定义在Zend/zend.h文件,其结构如下:

typedef struct _zval_struct zval;
...
struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount__gc;
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};

PHP使用这个结构来存储变量的所有数据。和其他编译性静态语言不同, PHP在存储变量时将PHP用户空间的变量类型也保存在同一个结构体中。这样我们就能通过这些信息获取到变量的类型。

zval结构体中有四个字段,其含义分别为:

属性名 含义 默认值
refcount__gc 表示引用计数 1
is_ref__gc 表示是否为引用 0
value 存储变量的值
type 变量具体的类型

2.变量类型:

zval结构体的type字段就是实现弱类型最关键的字段了,type的值可以为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。 从字面上就很好理解,他们只是类型的唯一标示,根据类型的不同将不同的值存储到value字段。 除此之外,和他们定义在一起的类型还有IS_CONSTANT和IS_CONSTANT_ARRAY。

这和我们设计数据库时的做法类似,为了避免重复设计类似的表,使用一个标示字段来记录不同类型的数据。

常见的设计模式有哪些?

简单说下单例模式,注册树模式,适配模式,策略模式,观察者模式。

记住三私一公,最容易考察现场撸码的题。

请看本人文档
《汪春波-php技术博客-设计模式》

本作品采用《CC 协议》,转载必须注明作者和本文链接
嗨,我是波波。曾经创业,有收获也有损失。我积累了丰富教学与编程经验,期待和你互动和进步! 公众号:上海PHP自学中心 付费知识星球:破解面试:程序员的求职导师
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
司机 @ 欣昊玉
文章
274
粉丝
341
喜欢
558
收藏
1108
排名:64
访问:12.3 万
私信
所有博文
社区赞助商