内置 Web 服务器

警告

此 Web 服务器旨在帮助应用程序开发。它也可能用于测试目的或在受控环境中运行的应用程序演示。它并非旨在成为功能齐全的 Web 服务器。不应在公共网络上使用它。

CLI SAPI 提供了一个内置的 Web 服务器。

Web 服务器仅运行一个单线程进程,因此如果请求被阻塞,PHP 应用程序将停止。

URI 请求从 PHP 启动的当前工作目录提供服务,除非使用 -t 选项指定显式文档根目录。如果 URI 请求未指定文件,则返回给定目录中的 index.php 或 index.html。如果这两个文件都不存在,则将在父目录中继续查找 index.php 和 index.html,依此类推,直到找到一个文件或到达文档根目录。如果找到了 index.php 或 index.html,则返回它并将 $_SERVER['PATH_INFO'] 设置为 URI 的尾部。否则,将返回 404 响应代码。

如果在启动 Web 服务器时在命令行中给出 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 开始,内置 Web 服务器可以配置为派生多个工作进程,以便测试需要对内置 Web 服务器进行多个并发请求的代码。在启动服务器之前,将 PHP_CLI_SERVER_WORKERS 环境变量设置为所需的工作进程数。

注意此功能在 Windows 上不受支持。

警告

实验性功能适用于生产环境。通常,内置 Web 服务器适用于生产环境。

示例 #1 启动 Web 服务器

$ 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

在对 http://localhost:8000/ 和 http://localhost: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 文件的请求将显示“欢迎使用 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 Web 服务器使用情况

在开发中使用 CLI Web 服务器并随后也使用生产 Web 服务器时重复使用框架路由器脚本

<?php
// router.php
if (php_sapi_name() == 'cli-server') {
/* 路由静态资产并返回 false */
}
/* 继续进行正常的 index.php 操作 */
?>
$ php -S localhost:8000 router.php

示例 #5 处理不支持的文件类型

如果您需要提供 CLI Web 服务器未处理的 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 Web 服务器

您可以使用以下命令使 Web 服务器在端口 8000 上对任何接口可用

$ php -S 0.0.0.0:8000
警告

不应在公共网络上使用内置 Web 服务器。

添加备注

用户贡献的备注 16 个备注

jonathan at reinink dot ca
10 年前
为了设置特定于项目的配置选项,只需将 php.ini 文件添加到您的项目中,然后使用以下标志运行内置服务器

php -S localhost:8000 -c php.ini

这对于不能在运行时设置的设置(ini_set())特别有用。
Mark Simon
8 年前
它没有直接提到,可能也不明显,但您也可以使用它来创建虚拟主机。当然,这需要您 hosts 文件的帮助。

以下是步骤

1 /etc/hosts
127.0.0.1 www.example.com

2 cd [根文件夹]
php -S www.example.com:8000

3 浏览器
http://www.example.com:8000/index.php

结合简单的 SQLite 数据库,您就拥有了一个非常方便的测试环境。
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 模式启动服务器。
tamas at bartatamas dot hu
9 年前
如果您的 URI 包含一个点,在使用内置 Web 服务器时,您将丢失 $_SERVER['PATH_INFO'] 变量。
我想写一个 API,并在 URI 中使用 .json 后缀,但框架的路由机制却失效了,我花了很多时间才发现原因是路由器依赖于 $_SERVER['PATH_INFO']。

参考资料
https://bugs.php.net/bug.php?id=61286
matthes at leuffen dot de
7 年前
要在命令行上输出调试信息,可以将输出写入 php://stdout

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

file_put_contents("php://stdout", "\nRequested: $path");
echo
"<p>Hello World</p>";
?>
Ivan Ferrer
11 年前
在 Windows 上,您可能发现 shell:sendto 中有一个 phpserver.bat 文件很有用:
explorer http://localhost:8888
rem 检查参数是文件还是目录
if exist "%~1\" (
php -S localhost:8888 -t "%~1"
) else (
php -S localhost:8888 -t "%~dp1"
)

然后进行快速 Web 测试,您只需要将文件或文件夹发送到此 bat 文件,它将打开您的资源管理器并运行服务器。
deep at deepshah dot me
4 年前
监听 IPv4 的所有地址
php -S 0.0.0.0:80

监听 IPv6 的所有地址
php -S [::0]:80
sony at sony-ak dot com
4 年前
要发送环境变量,只要使用 PHP 内置 Web 服务器,输入以下命令。

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

在 PHP 脚本中,我们可以使用以下代码进行检查。

<?php
echo getenv('MYENV'); // print dev
dachund at gmail dot com
6 年前
我使用内部 Web 服务器时,在处理没有点和文件扩展名的静态文件方面遇到了一些问题。

对于 URI 像 "/testfile" 这样的文件,Web 服务器响应了 200,但没有内容。

我不确定这是否是错误,但我创建了一个 router.php,现在它不使用 "return false;" 操作来让内部 Web 服务器传递静态文件。

我改为使用 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" does not exist', $_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;
dwingardjr at gmail dot com
7 年前
注意:对于使用 Windows 8.1 的用户,或者在运行 PHP 服务器 CLI 时遇到此问题的任何用户。

`PHP -S localhost:8000 -t /public` <-- 不起作用。

`PHP -S localhost:8000 -t public` <-- 起作用!

而且,在笔记中还提到了一些其他内容,说你不能同时为项目文件夹和路由文件提供服务。嗯,实际上是可以的!至少对我来说是可以的。

`PHP -S localhost:8000 router.php -t public` <-- 也许有人尝试了此方法,但它不起作用。

`PHP -S localhost:8000 -t public router.php` <-- 起作用!
devoldemar
7 个月前
内置 Web 服务器使用 SAPI 日志记录子系统。因此,所有消息都写入标准错误,而不是标准输出流。
如果要将服务器日志保存到文件中,以下命令将起作用
php -S 0.0.0.0:80 2>&1 | tee out.log
gyunaev at gmail dot com
7 年前
您还可以通过 error_log() 将消息打印到服务器的 STDOUT。

此外,文档没有明确说明,当您使用路由器脚本时,如果请求了 PHP 文件,并且您返回 false,则该 PHP 文件将被提供服务(即,您不需要手动加载和执行它)。
Lukas
5 年前
对于提供像 .css 或 .js 这样的静态内容,并且使用路由器(对我来说是 index.php),它对我来说开箱即用。

php -S localhost:8000

这是因为我的路由器文件是 index.php。但是

php -S localhost:8000 index.php

不起作用,因为我的静态文件不是通过我的路由器提供的。
Anonymous
3 年前
如果您在使用包含点的动态路由(出现意外的 404 错误)和静态文件托管的项目时遇到问题,请将此代码粘贴到您的 index.php 中

// 支持用于本地开发的 cli 服务器
if (php_sapi_name() === 'cli-server') {
$fileName = __DIR__.parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if (file_exists($fileName) && !is_dir($fileName)) return false;
}

然后直接在文件中运行内部服务器

php -S 127.0.0.1 index.php
ohcc at 163 dot com
7 年前
当使用 php 作为内置命令行 Web 服务器时,$_SERVER['SERVER_ADDR'] 未定义,因此您无法使用 $_SERVER['SERVER_ADDR'] 来检测服务器的 IP 地址。

附注:这在 2016-12-22 使用 PHP 7.1 的 Windows 上测试过。

以下是打印的 $_SERVER 变量。

数组
(
[DOCUMENT_ROOT] => E:\Programs\PHPServer\www\srv
[REMOTE_ADDR] => 118.117.61.32
[REMOTE_PORT] => 10865
[SERVER_SOFTWARE] => PHP 7.1.0 Development Server
[SERVER_PROTOCOL] => HTTP/1.1
[SERVER_NAME] => 0.0.0.0
[SERVER_PORT] => 8080
[REQUEST_URI] => /
[REQUEST_METHOD] => GET
[SCRIPT_NAME] => /index.php
[SCRIPT_FILENAME] => E:\Programs\PHPServer\www\srv\index.php
[PHP_SELF] => /index.php
[HTTP_HOST] => www.wuxiancheng.cn:8080
[HTTP_CONNECTION] => keep-alive
[HTTP_CACHE_CONTROL] => max-age=0
[HTTP_UPGRADE_INSECURE_REQUESTS] => 1
[HTTP_USER_AGENT] => Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
[HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
[HTTP_DNT] => 1
[HTTP_ACCEPT_ENCODING] => gzip, deflate, sdch
[HTTP_ACCEPT_LANGUAGE] => zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
[HTTP_COOKIE] => qbbs_2132_saltkey=fZ7509n5; qbbs_2132_lastvisit=1482156014; Hm_lvt_f812a4362ef73c80c4d13485d1ab3a49=1482159614; _ga=GA1.2.1594404236.1482159615; su=727vL6EEPLqjcyfJcad-za9eVYOh1i7e; Hm_lvt_6a65b0f2004e441e86ecea9c3562d997=1482232509,1482241896,1482242293,1482296586
[REQUEST_TIME_FLOAT] => 1482390410.65625
[REQUEST_TIME] => 1482390410
)
eyecatchup at gmail dot com
7 年前
注意:内置 Web 服务器有文件大小限制。对于大于 5 GB 的文件,它将始终显示“文件未找到”错误页面。
To Top