---
# System prepended metadata

title: PHP 運作
tags: [PHP]

---

 # PHP 運作

###### tags: `PHP`


## Server 運作原理

- 當 webserver 收到請求，會先確認請求的檔案是什麼，如果是靜態檔案，將會從系統中找出文件位置，發送回呼叫端。

- 如果請求的檔案是 php，將會交給 PHP 解析器處理，CGI 協議規範 server 跟 PHP 解析器數據交換格式，PHP 解析處理完成後，再返回資料給 web server，web server 再發送回呼叫端，PHP 解析器有兩種，官方的 Zend Engine 跟 FB 開發的 HHVM。


```graphviz
digraph G
{
    node[shape=box];
	client [label="呼叫端"];
    server [label="Web Server"]
    client->server
    
    static [label="靜態檔案(.html / .js)"]
    php [label="php 檔案"]
    
    server->static
    server->php
    
    parser [label="PHP 解析器(Zend Engine/HHVM)"]
    php->parser [label="CGI 協議"]
}
```

### Nginx

nginx 常用來做負載均衡跟反向代理等工作，不依賴 thread 去處理 request，而是使用非同步的 event-driven 架構

nginx 有區分 master 跟 worker，master 管理著 worker，啟動後會 fork 出多個 worker process 待命，在 config 設定中的 `worker_processes` 可以設定 worker process 數目，如果設定成 `auto` 會自動設定成 CPU 核心數

圖中可看出 naginx 啟用 1 個  master process 跟 2 個 worker process
![](https://i.imgur.com/7iuSONp.png)

worker process 會監聽事件，當有 client 端觸發請求 enent 時，worker process 會去處理分析請求並轉發至 I/O 操作或 PHP 解析器，針對 I/O 操作，nginx 使用的是 [epoll I/O 事件通知](https://zh.wikipedia.org/wiki/Epoll)，PHP 解析器下面會做說明。worker process 會監聽相關處理完成的 evnet，再回覆給 client 端


## PHP-FPM

早期 PHP 解析器運作會根據 php.ini 設定，初始化執行環境，然後處理 server 端送來的請求，處理完後關閉 Process。

但每次請求都要重新初始環境，速度太慢，因此有了 PHP-FPM 管理 PHP Process，PHP-FPM 會在服務啟動時建立 Master 跟數個 Workers，而 Web Server 會透過 TCP socket 或 Unix Socket 發送資料給 PHP-FPM Master，Master 再去分配給底下的 Workers 執行，執行完畢再透過 Master 回覆給 Web Server。

PHP-FPM 可控制記憶體使用量、workers 數目，還可平滑重載 php.ini 等配置，新的 worker 使用新的配置。

例如 nginx server 的 `fastcgi_pass` 配置 `unix:/run/php-fpm/php-fpm.sock`，就是透過 Unix Socket 發送給在本地的 php-fpm，如果改成 `{php-fpm-ip}:{php-fpm-port}`，就是透過 TCP socket 發送給其他 php-fpm server。


```graphviz
digraph G
{
    node[shape=box];
    server [label="Web Server"]
     
    master [label="PHP-FPM Master"]   
    worker_1 [label="PHP-FPM Worker 1"]
    worker_2 [label="PHP-FPM Worker 2"]
    worker_3 [label="PHP-FPM Worker 3"]
    worker_4 [label="PHP-FPM Worker 4"]
    
    server->master [label="FastCGI 協議"]
    master->worker_1
    master->worker_2
    master->worker_3
    master->worker_4
}
```

## Zend Engine

PHP 初始化會啟動 Zend 引擎，並載入擴展模塊，執行 php 文檔主要分成兩大步驟，預編譯(compile)以及執行(excuse)，預編譯會進行詞法(lex/tokenize)跟語法(parse)分析，生成 opcode，之後再執行 opcode 並輸出結果。

![](https://i.imgur.com/uJLvdAd.png)


每個 worker 在解析 php 文檔時都會執行預編譯，生成 opcode，假設文檔沒有更動，opcode 其實是一樣的，預編譯過程會浪費時間，因此出現了 opcache ，其實就是把該文檔的 opcode cache 起來，下一次執行相同文檔時，不必再編譯一次。當然，如果文檔有更新，必須清除 cache

可以透過 `opcache.validate_timestamps` & `opcache.revalidate_freq` 設定是否檢查文檔及檢查文檔的頻率。

使用 opcache 要注意，在高流量的情況下，重建 opcache 是非常消耗資源的一件事，因此正式環境必須考思考程式更新及 opcache 重置的問題。





