![](https://img.kancloud.cn/29/1f/291f59a028d3b6ed5ef3055aa87865c6_800x320.png)
## 目录
[TOC]
## 表单提交中的Get和Post的异同点
get 请求一般用于向服务端获取数据,post 一般向服务端提交数据
get 传输的参数在 url 中,传递参数大小有限制,post 没有大小限制,
get 不安全,post 安全性比get高
get请求在服务端用Request.queryString 接受 ,post 请求在服务端用Requset.form 接受
## echo(),print(),print_r()的区别
echo是PHP语句, print和print\_r是函数,语句没有返回值,函数可以有返回值(即便没有用)
print() 只能打印出简单类型变量的值(如int,string)
print\_r() 可以打印出复杂类型变量的值(如数组,对象)
echo 输出一个或者多个字符串
## 数组[‘a’, ‘b’, ‘c’] 转换成字符串 ‘abc’
~~~
echo implode(‘’,[‘a’, ‘b’, ‘c’]);
echo join([‘a’, ‘b’, ‘c’],'');
~~~
## 获取字符串’aAbB’中A首次出现的位置
~~~
$str=‘aAbB’;
echo strpos($str,"A");
~~~
## 编写一段用最小代价实现将字符串完全反序, e.g. 将 “1234567890” 转换成 “0987654321”.
~~~
(1)使用函数
echo strrev("Hello World!");
(2) 不使用函数
$s = '1234567890';
$o = '';
$i = 0;
while(isset($s[$i]) && $s[$i] != null) {
$o = $s[$i++].$o;
}
echo $o;
~~~
## 请用递归实现一个阶乘求值算法 F(n): n=5;F(n)=5!=5*4*3*2*1=120
~~~
function F($n){
if($n==0){
return 1;
}else{
return $n* F($n-1);
}
}var_dump(F(5));
~~~
## 将字符长fang-zhi-gang 转化为驼峰法的形式:FangZhiGang
//方法一
~~~
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); // 根据键,以降序对关联数组进行排序
~~~
可参考:https://www.runoob.com/php/php-arrays-sort.html
## 用PHP写出显示客户端IP与服务器IP的代码
~~~
$_SERVER["REMOTE_ADDR"]
$_SERVER["SERVER_ADDR"]
~~~
## 语句include和require的区别是什么?为避免多次包含同一文件,可用(?)语句代替它们?
1、加载失败的处理方式不同
include与require除了在处理引入文件的方式不同外,最大的区别就是:
include在引入不存在的文件时,产生一个警告且脚本还会继续执行,
require则会导致一个致命性错误且脚本停止执行。
`<?php`
`include 'hello.php';`
`echo 'world';`
`?>`
如果hello.php不存在,echo ‘world’这句是可以继续执行的。
`<?php`
`require 'hello.php';`
`echo 'world';`
`?>`
如果hello.php不存在,echo ‘hello’这句是不会执行的,到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 "文件包含失败";`
`}`
可以用include\_once,require\_once代替,表示文件只引入一次,引入之后则不在引入,作为优化点
## PHP 不使用第三个变量实现交换两个变量的值
~~~
list($b,$a)=array($a,$b);
var_dump($a,$b);
~~~
## 写一个方法获取文件的扩展名
~~~
//方法一
function get_extension($file){
return substr(strrchr($file,'.'), 1);
}
//方法二
function get_extension($file){
return end(explode('.', $file));
}
echo get_extension('fangzhigang.png'); //png
~~~
## 用PHP打印出前一天的时间格式是2017-3-22 22:21:21
~~~
$a = date("Y-m-d H:i:s", strtotime("-1 days"));
~~~
## PHP 如何接口调用?
使用curl调用http接口:
~~~
public function authenticationApi($data,$url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
//输出格式可以转为数组形式的json格式
$tmpInfo = curl_exec($ch);
curl_close($ch);
return $tmpInfo;
}
~~~
## 用PHP header()函数实现页面404错误提示功能
~~~
Header("HTTP/1.1 404 Not Found");
~~~
## composer是什么?Composer和PHP有什么关系?
Composer是PHP的一个依赖(dependency)管理工具,在我们的项目中声明所依赖的外部工具库(libraries),Composer 可以帮助我们安装这些依赖的库文件。Composer可以全局安装也可以局部安装,默认不是全局安装的,是基于指定项目的某个目录进行安装的。
## composer团队协作怎么保证版本统一?
安装组件使用composer install 而不是composer update,
.lock文件加入版本控制当中。
## OOP思想,特征和其意义
抽象、封装、继承和多态是面向对象的基础。
抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。
封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)
继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。
多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。
帮助理解: https://www.cnblogs.com/waj6511988/p/6974291.html
## OOP的七大设计原则是什么?
* 开闭原则:对扩展开放,对修改关闭
* 里氏替换原则:继承 必须保证 父类中的性质在子类中仍然成立
* 依赖倒置原则:面向接口编程,而不面向实现类
* 单一职责原则:控制 类的 粒度的大小 ,增强内聚性,减少耦合
* 接口隔离原则:要为各个类提供所需的专用接口
* 迪米特法则:迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD。
* 合成复用原则:尽可能使用组合或者聚合等关系来关联类,其次才考虑使用继承。
前五个合称 SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则)
## mvc框架的生命周期说一下
* model:存放向数据库请求来的数据
* view:存放组件、图片、页面模板html文件
* controller:获取或改变model里的数据返回给页面渲染数据
用户请求进来,先加载配置文件,框架初始化,然后匹配路由地址,寻找到对应的controller的文件地址,引入加载文件,实例化controller,根据路由匹配得到的方法和参数,调用并传参到方法,此处可能需要读取db,model层则负责数据库存取,提供封装好的方法给到controller层调用,controller层得到数据后,通过引入view层文件,传递数据到view层,渲染html模板后输出。
![](https://img.kancloud.cn/8f/e2/8fe26fc6abd5efbda1eb1163209adca0_694x509.png)
可以参考:https://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?
1. sessionID存储在cookie中,若要攻破session首先要攻破cookie;
2. sessionID是要有人登录,或者启动session\_start才会有,所以攻破cookie也不一定能得到sessionID;
3. 第二次启动session\_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。
4. sessionID是加密的
5. 综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。
## session与cookie的应用场景有哪些?
cookie:
(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(2)保存上次登录的时间等信息。
(3)保存上次查看的页面。
(4)浏览计数。
session:
Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
(1)网上商城中的购物车。
(2)保存用户登录信息。
(3)将某些数据放入session中,供同一用户的不同页面使用。
(4)防止用户非法登录。
## php7新特性
标量类型声明
返回值类型声明
语法糖:null合并运算符,太空船操作符
define允许定义常量数组,
匿名类,
新增了一些函数intdiv(),随机函数,
**1、php7.0相比于php5.6的新特性**
参考:http://php.net/manual/zh/migration70.new-features.php
**2、php7.1相对于php7.0的新特性**
参考:http://php.net/manual/zh/migration71.new-features.php
**3、php7.2相对于php7.1的新特性**
参考:http://php.net/manual/zh/migration72.new-features.php
## php8新特性
新增联合类型(Union Types);
添加了 WeakMap;
添加了 ValueError 类;
新增的特性大多是语法糖,主要是JIT。
JIT是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行,在PHP8中,Zend VM不需要解释某些操作码,并且这些指令将直接作为CPU级指令执行。
**IT和opcache区别**
要说明opcode cache与JIT的区别,得先明白,**字节码,又叫中间码与机器码的区别。**
简答的说,提升php执行效率,更快了。
JIT参考[鸟哥博客](https://www.laruence.com/2020/06/27/5963.html)
## 手写一个单例模式吧
所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中!
单例设计模式常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。
单例模式的编写遵循**三私一公**,代码如下
~~~
<?php
class Database {
private $instance;
private function__construct() {
// Do nothing.
}
private function__clone() {
// Do nothing.
}
public static function getInstance() {
if (!(self::$instance instanceof self)) {
self::$instance = new self();
}
return self::$instance;
}
}
$a = Database::getInstance();
$b = Database::getInstance();
// true var_dump($a === $b);
~~~
## php垃圾回收机制
>关键词:使用了引用计数器
1. PHP可以自动进行内存管理,清除不需要的对象,主要使用了引用计数。
2. 在zval结构体中定义了ref\_count和is\_ref , ref\_count是引用计数 ,标识此zval被多少个变量引用 , 为0时会被销毁 。
is\_ref标识是否使用的 &取地址符强制引用。
3. 为了解决循环引用内存泄露问题 , 使用同步周期回收算法。
4. 当数组或对象循环的引用自身 , unset掉数组的时候 , 当refcount-1后还大于0的 , 就会被当成疑似垃圾 , 会进行遍历 ,并且模拟的删除一次refcount-1如果是0就删除 ,如果不是0就恢复。
参考:https://www.php.net/manual/zh/features.gc.php
## php-fpm是什么?
PHP5.3.3开始集成了php-fpm模块,不再是第三方的包了。PHP-FPM提供了更好的PHP[进程管理](https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86)方式,可以有效控制内存和进程、可以平滑[重载](https://baike.baidu.com/item/%E9%87%8D%E8%BD%BD)PHP配置。
重点:
>php-fpm是fastcgi的实现。
## 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进程一次只能处理一个请求。
![](https://img.kancloud.cn/41/ed/41ed99b258dd1703acd803c8de18db1f_640x226.png)
## 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进行通信。有两种方式;
1. TCP
2. 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
~~~csharp
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
~~~
1. 当空闲进程数小于min\_spare\_servers时,创建新的子进程,总子进程数小于等于pm.max\_children,小于等于process.max
2. 当空闲进程数大于max\_spare\_servers,会杀死启动时间最长的子进程
3. 如果子进程(idle状态)数大于max\_children,会打印warning日志,结束处理
4. process小于 max\_children ,计算一个num,启动num个worker
5. 优点:动态扩容,不浪费系统资源
6. 缺点:所有worker都在工作,新的请求到来需要等待创建worker进程,最长等待1s(内部存在一个1s的定时器,去查看,创建进程),频繁启停进程消耗cpu,请求数稳定,不需要频繁销毁
**静态**
启动固定大小数量的worker,也有1s的定时器,用于统计进程的一些状态信息,例如空闲worker个数,活动worker个数
~~~bash
pm.max_children = 10 #必须配置这个参数,而且只有这个参数有效
~~~
1. 优点:不用动态判断负载,提升性能
2. 缺点:如果配置成static,只需要考虑max\_children数量,数量取决于cpu的个数和应用的响应时间,一次启动固定大小进程浪费系统资源
**按需分配**
php-fpm启动的时候不会启动worker进程,按需启动worker,有链接进来后,才会启动
~~~undefined
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 是单入口文件的mvc重型框架,里面集成了消息队列,orm,ioc等功能模块。laravel的生命周期简述如下:
从`public\index.php`开始。
注册加载composer自动生成的`class loader`,初始化第三方依赖,laravel本身实现的也已经高度组件化了。
使用上一步生成的容器`Container`类,从`bootstrap/app.php`脚本获取 Laravel 应用实例,注册laravel核心组件。
如果是web请求不是命令行请求,那么**创建 Laravel HTTP Kernel 核心**,请求被发送到`HTTP`内核。
HTTP 内核继承自 Illuminate\\Foundation\\Http\\Kernel 类,这个类中拥有三个核心成员变量 $app,$router,$bootstrappers。
~~~
protected $app;
protected $router;
protected $bootstrappers \= \[
\\Illuminate\\Foundation\\Bootstrap\\LoadEnvironmentVariables::class, # 加载 .env 中的配置信息
\\Illuminate\\Foundation\\Bootstrap\\LoadConfiguration::class, # 加载 config 目录中所有配置文件的配置信息
\\Illuminate\\Foundation\\Bootstrap\\HandleExceptions::class, # 异常处理
\\Illuminate\\Foundation\\Bootstrap\\RegisterFacades::class, # 注册门面
\\Illuminate\\Foundation\\Bootstrap\\RegisterProviders::class, # 注册Service Providers
\\Illuminate\\Foundation\\Bootstrap\\BootProviders::class, # 注册启动器
\];
~~~
$app 咱们已经说了足够多,就是应用容器,我们所有的工作都在这个容器中进行。
$router 路由对象,它提供路由相关的服务,帮助我们把网络请求分配给对应的路由进行逻辑处理,然后把处理的结果(网络响应)返回给我们,我们在web.php中定义的路由就是由它来管理的。
$bootstrappers 数组,这个数组中的任务项在网络请求被处理前运行,我们可以看到**环境检查,配置加载,异常处理,Facedes 门面注册,ServiceProvider 注册**等等任务都需要在网络请求被处理前被首先执行,而且这些任务是有前后顺序的,排在前面的会首先执行,这也很容易理解,因为不管是 Facades 还是 Service Providers 都是定义在 config 目录中的 app.php 文件中的,只有加载来配置之后才能注册门面和Service Providers。
结束请求,进行回调,终止 Laravel 应用,Laravel 到此也完成了它的历史使命。中间件中有一类的中间件,terminable middleware 的处理逻辑就是在这个阶段执行的。
**扩展知识【中间件】**
Middleware ,在 Kernel 以及它的基类 Illuminate\\Foundation\\Http\\Kernel 中定义了一系列的 middlewares ,借助这些中间件,就可以完成对用户请求的过滤和安全检查等等功能。
~~~
public function __construct(Application $app, Router $router)
{
$this->app = $app;
$this->router = $router;
$route->middlewarePriority = $this->middlewarePriority;
foreach ($this->middlewareGroups as $key => $middleware) {
$router->middlewareGroup($key, $middleware);
}
foreach ($this->routeMiddleware as $key => $middleware) {
$router->aliasMiddleware($key, $middleware);
}
}
~~~
中间件的作用:所有请求在处理前需要经过的 HTTP[中间件](https://laravel.com/docs/5.4/middleware),这些中间件处理[HTTP 会话](https://laravel.com/docs/5.4/session)的读写、判断应用是否处于维护模式、验证[CSRF 令牌](https://laravel.com/docs/5.4/csrf)等等。
![](https://img.kancloud.cn/1d/1e/1d1e54555ca319d5aaee750cec85661f_572x508.png)
**laravel请求生命周期流程图,如下**
![](https://img.kancloud.cn/92/26/922612cbdd637e1d8f8210b16558b445_1748x696.png)
## 怎么理解 依赖注入(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
//产品
class bike
{
public function product($destination)
{
print_r($destination);
}
}
class car
{
public function product($destination)
{
print_r($destination);
}
}
//工厂
class simpleFactory
{
public function createBike($obj)
{
return new $obj();
}
}
$factory = new simpleFactory();
$bike = $factory->createBike("bike");
$bike->product("hello,bike");
$car = $factory->createBike("car");
$car->product("hello,car");
?>
~~~
**观察者模式**
~~~
//观察者模式
//适用场景:订阅者通知
//自己对观察者模式对理解:
需求:有事情变化你要通知我
实现:
1、要通知对人必须在我这里注册,否则我不知道要通知谁。
2、想要获取通知的人必须遵守我的规则,实现我指定的通知事件,不然我没办法统一通知,(比如高考查分数,只支持电话查询,你非要微信查我肯定通知不到你)
3、事件发生的时候我会逐一通知你们。
<?php
//定义一个事件产生接口
abstract class genEvent
{
private $ob_servers = [];
//增加观察者
public function addObs($ob_server)
{
$this->ob_servers[] = $ob_server;
}
//通知
public function notify()
{
if(!empty($this->ob_servers))
{
foreach($this->ob_servers as $ob_server)
{
$ob_server->update();
}
}
}
}
//定义观察者接口
interface obServer{
public function update($event_info = null);
}
class obServer1 implements obServer{
public function update($event_info = null){
echo "观察者1 收到执行通知\n";
}
}
class obServer2 implements obServer{
public function update($event_info = null){
echo "观察者2 收到执行通知\n";
}
}
class event extends genEvent{
//事件触发
public function trigger()
{
$this->notify();
}
}
//实现
$event = new event();
$event->addObs(new obServer1());
$event->addObs(new obServer2());
$event->trigger();
?>
~~~
**适配器模式**
~~~
<?php
//定义抽象类
abstract class Toy
{
public abstract function openMouth();
public abstract function closeMouth();
}
//定义dog
class dog extends Toy
{
public function openMouth()
{
echo "Dog open Mouth\n";
}
public function closeMouth()
{
echo "Dog close Mouth\n";
}
}
//cat
class cat extends Toy
{
public function openMouth()
{
echo "Cat open Mouth\n";
}
public function closeMouth()
{
echo "Cat close Mouth\n";
}
}
//红枣狗可以自己判断开关
interface redTarget
{
public function doMouthOpen();
public function doMouthClose();
}
interface greenTarget
{
public function operateMouth($type=0);
}
//组成适配器
class redAdapter implements redTarget
{
private $adaptee;
//初始化对象
public function __construct(Toy $adaptee)
{
$this->adaptee = $adaptee;
}
//委派调用Adaptee的sampleMethod1方法
public function doMouthOpen()
{
$this->adaptee->openMouth();
}
public function doMouthClose()
{
$this->adaptee->closeMouth();
}
}
//组成绿色遥控
class greenAdapter implements greenTarget
{
private $adapter;
//初始化对象
public function __contruct(Toy $adapter)
{
$this->adapter = $adapter;
}
public function operateMouth($type = 0)
{
$type ? $this->adapter->openMouth() : $this->adapter->closeMouth();
}
}
//测试
class testDriver
{
public function run()
{
//实例化玩具狗
$dog = new dog();
$adapter_dog = new redAdapter($dog);
$adapter_dog->doMouthOpen();
$adapter_dog->doMouthClose();
}
}
$test = new testDriver();
$test->run();
?>
~~~
**注册树模式**
~~~
//创建单例
class Single{
public $hash;
static protected $ins=null;
final protected function __construct(){
$this->hash=rand(1,9999);
}
static public function getInstance(){
if (self::$ins instanceof self) {
return self::$ins;
}
self::$ins=new self();
return self::$ins;
}
}
//工厂模式
class RandFactory{
public static function factory(){
return Single::getInstance();
}
}
//注册树
class Register{
protected static $objects;
public static function set($alias,$object){
self::$objects[$alias]=$object;
}
public static function get($alias){
return self::$objects[$alias];
}
public static function _unset($alias){
unset(self::$objects[$alias]);
}
}
//调用
Register::set('rand',RandFactory::factory());
$object=Register::get('rand');
print_r($object);
~~~
- 导读
- 简历和信心
- 一面(技术基础)
- PHP
- MySQL-基础
- MySQL-基础2
- Nginx
- Redis
- 网络
- 二面(技术进阶)
- PHP
- MySQL
- Nginx
- Redis
- Linux
- 网络
- 算法
- 操作系统
- 数据结构
- 网络安全
- 分布式和微服务
- 线上故障处理经验
- 架构
- 通用型业务的解决方案
- 高并发下如何保持一致性?
- 软件测试的阶段和方法有哪些
- 雪崩效应解决方案
- 两个海量数据的同构表,如何查询数据差异?
- 怎么设计一套通信接口的标准?
- 工作中有用到ES么
- 如何设计SKU表结构
- 如何设计RBAC表结构
- 如何设计防超卖的架构
- 如何设计高并发的架构
- 怎么理解SaaS,如何设计SaaS项目的架构
- 如何设计新浪关注feed流的架构
- 怎么设计短url服务
- 如何实现接口幂等性?
- 如何设计高可用的订单业务架构
- 如何设计单点登录的架构
- 如何应对大流量高并发?
- 团队开发中,git分支管理怎么设定策略?
- 项目设计
- 如何设计秒杀架构?
- 有哪些方式生成唯一ID?
- 三面(技术终面)
- 工作素养
- 四面(hr & hrbp)
- 离职前要先找好下家吗?
- 离职原因
- 谈薪定级
- 个人道德、职业素养和服从性测试
- 反问环节
- 注意事项
- 扩展学习