
## 目錄
[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://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進程一次只能處理一個請求。

## 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)等等。

**laravel請求生命周期流程圖,如下**

## 怎么理解 依賴注入(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)
- 離職前要先找好下家嗎?
- 離職原因
- 談薪定級
- 個人道德、職業素養和服從性測試
- 反問環節
- 注意事項
- 擴展學習