PHP Conference Japan 2024

內建網路伺服器

警告

此網路伺服器旨在輔助應用程式開發。它也可能適用於測試目的或在受控環境中執行的應用程式示範。它並非設計用於功能齊全的網路伺服器。不應在公共網路上使用。

CLI SAPI 提供內建的網路伺服器。

網路伺服器僅執行單一執行緒的行程,因此如果請求被封鎖,PHP 應用程式將會停滯。

URI 請求會從 PHP 啟動時的目前工作目錄提供服務,除非使用 -t 選項指定明確的 文件根目錄。如果 URI 請求未指定檔案,則會傳回指定目錄中的 index.php 或 index.html。如果兩個檔案都不存在,則會在父目錄中繼續搜尋 index.php 和 index.html,依此類推,直到找到其中一個或到達文件根目錄為止。如果找到 index.php 或 index.html,則會傳回該檔案,並將 $_SERVER['PATH_INFO'] 設定為 URI 的尾端部分。否則會傳回 404 回應碼。

如果在啟動網頁伺服器時在命令列上指定了 PHP 檔案,則該檔案會被視為「路由器」腳本。此腳本會在每個 HTTP 請求開始時執行。如果此腳本傳回 **false**,則會按原樣傳回所請求的資源。否則,腳本的輸出會傳回瀏覽器。

具有以下副檔名的檔案會傳回標準 MIME 類型:.3gp.apk.avi.bmp.css.csv.doc.docx.flac.gif.gz.gzip.htm.html.ics.jpe.jpeg.jpg.js.kml.kmz.m4a.mov.mp3.mp4.mpeg.mpg.odp.ods.odt.oga.ogg.ogv.pdf.png.pps.pptx.qt.svg.swf.tar.text.tif.txt.wav.webm.wmv.xls.xlsx.xml.xsl.xsd.zip

從 PHP 7.4.0 開始,內建網頁伺服器可以設定為 fork 多個 worker,以便測試需要多個併發請求到內建網頁伺服器的程式碼。在啟動伺服器之前,將 PHP_CLI_SERVER_WORKERS 環境變數設定為所需的 worker 數量。

注意Windows 不支援此功能。

警告

此*實驗性*功能*不*適用於生產環境。一般來說,內建網頁伺服器*不*適用於生產環境。

範例 #1 啟動網頁伺服器

$ cd ~/public_html
$ php -S localhost:8000

終端機將顯示

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit

在對 https://127.0.0.1:8000/ 和 https://127.0.0.1:8000/myscript.html 發出 URI 請求後,終端機將顯示類似以下內容:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit.
[Thu Jul 21 10:48:48 2011] ::1:39144 GET /favicon.ico - Request read
[Thu Jul 21 10:48:50 2011] ::1:39146 GET / - Request read
[Thu Jul 21 10:48:50 2011] ::1:39147 GET /favicon.ico - Request read
[Thu Jul 21 10:48:52 2011] ::1:39148 GET /myscript.html - Request read
[Thu Jul 21 10:48:52 2011] ::1:39149 GET /favicon.ico - Request read

請注意,在 PHP 7.4.0 之前,除非路由器腳本處理這些符號連結的靜態資源,否則在 Windows 上無法存取它們。

範例 #2 使用特定文件根目錄啟動

$ cd ~/public_html
$ php -S localhost:8000 -t foo/

終端機將顯示

PHP 5.4.0 Development Server started at Thu Jul 21 10:50:26 2011
Listening on localhost:8000
Document root is /home/me/public_html/foo
Press Ctrl-C to quit

範例 #3 使用路由器腳本

在此範例中,對圖片的請求將顯示圖片,但對 HTML 檔案的請求將顯示「Welcome to PHP」。

<?php
// router.php
if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"])) {
return
false; // 直接提供請求的資源。
} else {
echo
"<p>Welcome to PHP</p>";
}
?>
$ php -S localhost:8000 router.php

範例 #4 檢查 CLI 網頁伺服器的使用

在開發過程中使用 CLI 網頁伺服器,以及稍後在正式網頁伺服器上重複使用框架路由器腳本

<?php
// router.php
if (php_sapi_name() == 'cli-server') {
/* 路由靜態資源並返回 false */
}
/* 繼續執行一般的 index.php 操作 */
?>
$ php -S localhost:8000 router.php

範例 #5 處理不支援的檔案類型

如果您需要提供 CLI 網頁伺服器未處理的 MIME 類型的靜態資源,請使用

<?php
// router.php
$path = pathinfo($_SERVER["SCRIPT_FILENAME"]);
if (
$path["extension"] == "el") {
header("Content-Type: text/x-script.elisp");
readfile($_SERVER["SCRIPT_FILENAME"]);
}
else {
return
FALSE;
}
?>
$ php -S localhost:8000 router.php

範例 #6 從遠端機器存取 CLI 網頁伺服器

您可以使用以下指令,讓任何介面都能透過 8000 連接埠存取網頁伺服器

$ php -S 0.0.0.0:8000
警告

內建網頁伺服器不應在公開網路上使用。

新增註記

使用者貢獻的註記 10 則註記

128
jonathan at reinink dot ca
10 年前
要設定專案特定的配置選項,只需將 php.ini 檔案新增到您的專案中,然後使用以下旗標執行內建伺服器

php -S localhost:8000 -c php.ini

這對於無法在執行期間設定 (ini_set()) 的設定特別有用。
72
Mark Simon
8 年前
這並沒有直接提及,而且可能不太明顯,但您也可以使用它來建立虛擬主機。當然,這需要 hosts 檔案的協助。

步驟如下

1 /etc/hosts
127.0.0.1 www.example.com

2. 切換目錄至網站根目錄
php -S www.example.com:8000

3. 開啟瀏覽器
http://www.example.com:8000/index.php

搭配一個簡單的 SQLite 資料庫,您就有一個非常方便的測試環境。
55
oan at vizrt dot com
7 年前
我痛苦地經歷了一個似乎沒有在此記錄的行為,因此我想透過以下提醒來避免大家重蹈覆轍。

在 Mac(我的例子是 macOS Sierra)上啟動 php -S 來架設本地伺服器時,我遇到了從舊版 Java 連線的問題。

結果發現,如果您使用以下指令啟動 PHP 伺服器:
"php -S localhost:80"
伺服器將僅以 IPv6 支援啟動!

要透過 IPv4 存取它,您需要像這樣更改啟動指令:
"php -S 127.0.0.1:80"
這會僅以 IPv4 模式啟動伺服器。
29
tamas at bartatamas dot hu
10 年前
如果您的 URI 包含點號,則在使用內建網路伺服器時,您將遺失 $_SERVER['PATH_INFO'] 變數。
我想要編寫一個 API,並在 URI 中使用 .json 結尾,但後來框架的路由機制失效了,我花了很多時間才發現其背後的原因是它的路由器依賴於 $_SERVER['PATH_INFO']。

參考資料
https://bugs.php.net/bug.php?id=61286
22
matthes at leuffen dot de
8 年前
要在命令列上輸出除錯資訊,您可以將輸出寫入 php://stdout

<?php
$path
= $_SERVER["SCRIPT_FILENAME"];

file_put_contents("php://stdout", "\n請求: $path");
echo
"<p>Hello World</p>";
?>
27
Ivan Ferrer
11 年前
在 Windows 上,您可能會發現將 phpserver.bat 檔案放在 shell:sendto 中很有用,內容如下:
explorer https://127.0.0.1:8888
rem 檢查參數是檔案還是目錄
if exist "%~1\" (
php -S localhost:8888 -t "%~1"
) else (
php -S localhost:8888 -t "%~dp1"
)

然後,為了快速進行網頁測試,您只需將檔案或資料夾「傳送到」這個 bat 檔案,它就會開啟您的檔案總管並執行伺服器。
7
deep at deepshah dot me
4 年前
監聽所有 IPv4 位址
php -S 0.0.0.0:80

監聽所有 IPv6 位址
php -S [::0]:80
0
sony at sony-ak dot com
4 年前
要使用 PHP 內建網路伺服器傳送環境變數,請輸入如下內容:

~$ MYENV=dev php -d variables_order=EGPCS -S 0.0.0.0:8000

在 PHP 腳本中,我們可以使用以下程式碼進行檢查。

<?php
echo getenv('MYENV'); // 顯示 dev
-3
dachund at gmail dot com
6 年前
我試著用內建的網頁伺服器,但在處理沒有附檔名(沒有點號和副檔名)的靜態檔案時遇到了一些問題。

對於像 "/testfile" 這樣的 URI,網頁伺服器會回應 200 卻沒有任何內容。

我不確定這是不是一個 bug,但我建立了一個 router.php,它不再使用 "return false;" 操作來讓內建網頁伺服器處理靜態檔案。

我改用 fpassthru() 來處理。

除此之外,我的 router.php 還可以設定成...
- ... 在請求目錄時,指定特定的索引檔案
- ... 設定正規表達式路由,以便在 REQUEST_URI 符合正規表達式時,請求特定的檔案或目錄。(類似您在 nginx 設定檔或 .htaccess 的 ModRewrite 中會做的事情)

也許有人會覺得這很有幫助。

================================

<?php

$indexFiles
= ['index.html', 'index.php'];
$routes = [
'^/api(/.*)?$' => '/index.php'
];

$requestedAbsoluteFile = dirname(__FILE__) . $_SERVER['REQUEST_URI'];

// 檢查請求是否符合已定義的路由
foreach ($routes as $regex => $fn)
{
if (
preg_match('%'.$regex.'%', $_SERVER['REQUEST_URI']))
{
$requestedAbsoluteFile = dirname(__FILE__) . $fn;
break;
}
}

// 如果請求是一個目錄,則檢查索引檔案是否存在
if (is_dir($requestedAbsoluteFile))
{
foreach (
$indexFiles as $filename)
{
$fn = $requestedAbsoluteFile.'/'.$filename;
if (
is_file($fn))
{
$requestedAbsoluteFile = $fn;
break;
}
}
}

// 如果請求的檔案不存在或是一個目錄 => 404
if (!is_file($requestedAbsoluteFile))
{
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
printf('"%s" 不存在', $_SERVER['REQUEST_URI']);
return
true;
}

// 如果請求的檔案不是 php 檔案
if (!preg_match('/\.php$/', $requestedAbsoluteFile)) {
header('Content-Type: '.mime_content_type($requestedAbsoluteFile));
$fh = fopen($requestedAbsoluteFile, 'r');
fpassthru($fh);
fclose($fh);
return
true;
}

// 如果請求的檔案是 php 檔案,則引入它
include_once $requestedAbsoluteFile;
-6
devoldemar
11 個月前
內建網路伺服器使用 SAPI 記錄子系統。因此,所有訊息都會寫入標準錯誤,而不是標準輸出串流。
如果您想將伺服器日誌儲存到檔案中,可以使用以下指令:
php -S 0.0.0.0:80 2>&1 | tee out.log
To Top