内置Web Server

PHP 5.4.0起, CLI SAPI 提供了一个内置的Web服务器。

这个内置的Web服务器主要用于本地开发使用,不可用于线上产品环境。

URI请求会被发送到PHP所在的的工作目录(Working Directory)进行处理,除非你使用了-t参数来自定义不同的目录。

如果请求未指定执行哪个PHP文件,则默认执行目录内的index.php 或者 index.html。如果这两个文件都不存在,服务器会返回404错误。

当你在命令行启动这个Web Server时,如果指定了一个PHP文件,则这个文件会作为一个"路由"脚本,意味着每次请求都会先执行这个脚本。如果这个脚本返回 FALSE ,那么直接返回请求的文件(例如请求静态文件不作任何处理)。否则会把输出返回到浏览器。

Example #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,窗口会显示:

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

Example #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

Example #3 使用路由(Router)脚本

请求图片直接显示图片,请求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

执行之后终端显示:

PHP 5.4.0 Development Server started at Thu Jul 21 10:53:19 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit.
[Thu Jul 21 10:53:45 2011] ::1:55801 GET /mylogo.jpg - Request read
[Thu Jul 21 10:53:52 2011] ::1:55803 GET /abc.html - Request read
[Thu Jul 21 10:53:52 2011] ::1:55804 GET /favicon.ico - Request read

User Contributed Notes

arsensafarian1984 at gmail dot com 19-Oct-2017 12:40
submit_btn.addEventListener(MouseEvent.CLICK, sendMessage);
function sendMessage(e:MouseEvent):void{
var my_vars:URLVariables = new URLVariables();
my_vars.senderName = name_txt.text;
my_vars.senderEmail = email_txt.text;
my_vars.senderMsg = message_txt.text;

var my_url:URLRequest = new URLRequest("mail.php");
my_url.method = URLRequestMethod.POST;
my_url.data = my_vars;

var my_loader:URLLoader = new URLLoader();
my_loader.dataFormat = URLLoaderDataFormat.VARIABLES;
my_loader.load(my_url);

name_txt.text = "";
email_txt.text = "";
message_txt.text = "Message Sent";

}
dwingardjr at gmail dot com 10-Aug-2017 08:53
Just a note to people who also use windows 8.1, or anyone who has had this problem when running the using the PHP server CLI.

`PHP -S localhost:8000 -t /public` <-- Not going to work.

`PHP -S localhost:8000 -t public` <-- Works!

And there is something else up in the notes saying something about you can't serve a project folder and a router file. Well, actually you can! At least for me.

`PHP -S localhost:8000 router.php -t public` <-- Perhaps someone tries this and it doesn't work.

`PHP -S localhost:8000 -t public router.php` <-- Works!
gyunaev at gmail dot com 29-Jun-2017 04:16
You can also print messages to the server's STDOUT via error_log().

Also the documentation doesn't make it clear that when you use router script if a PHP file is requested and you return false, the PHP file will be served (i.e. you do not need to load and eval it manually).
eyecatchup at gmail dot com 27-May-2017 04:26
Note: The built-in web server has a file size limit. For files larger than 5 GB, it will always serve a "File not found" error page.
oan at vizrt dot com 11-Jan-2017 10:43
I painfully experienced behaviour that I can't seem to find documented here so I wanted to save everyone from repeating my mistake by giving the following heads up:

When starting php -S on a mac (in my case macOS Sierra) to host a local server, I had trouble with connecting from legacy Java.

As it turned out, if you started the php server with
"php -S localhost:80"
the server will be started with ipv6 support only!

To access it via ipv4, you need to change the start up command like so:
 "php -S 127.0.0.1:80"
which starts server in ipv4 mode only.
ohcc at 163 dot com 22-Dec-2016 04:09
$_SERVER['SERVER_ADDR'] is not defined when using php as the built-in commandline web server, so you can not use $_SERVER['SERVER_ADDR'] to detect the Server's IP address.

P.S.: This is tested on Windows with PHP 7.1 on 2016-12-22.

Below is the printed $_SERVER variable.

Array
(
    [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
)
matthes at leuffen dot de 18-Oct-2016 10:21
To output debugging information on the command line you can write output to php://stdout:

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

file_put_contents("php://stdout", "\nRequested: $path");
echo
"<p>Hello World</p>";
?>
ohcc at 163 dot com 18-Sep-2016 04:29
Do NOT use the -t option if you want to use a router script, you can change current working directory to the web document root before launching the php built-in web server.

[works fine]

cd /d C:\Web\Server
php -S www.51-n.com:80 router.php -n -d expose_php=0 -d display_errors=0 -d extension_dir="ext"

[always gets a 500 error]

php -S www.51-n.com:80 router.php -t C:\Web\Server -n -d expose_php=0 -d display_errors=0 -d extension_dir="ext"
Mark Simon 07-Aug-2016 06:55
It's not mentioned directly, and may not be obvious, but you can also use this to create a virtual host. This, of course, requires the help of your hosts file.

Here are the steps:

1    /etc/hosts
    127.0.0.1    www.example.com

2    cd [root folder]
    php -S www.example.com:8000

3    Browser:
    http://www.example.com:8000/index.php

Combined with a simple SQLite database, you have a very handy testing environment.
@salmanapk 15-Apr-2016 12:50
I improved Ivan Ferrer's phpserver.bat to open the Send To'd files in the browser directly:-

rem check if arg is file or dir
if exist "%~1\" (
  explorer http://localhost:8888
  php -S localhost:8888 -t "%~1"
) else (
  explorer http://localhost:8888/%~nx1
  php -S localhost:8888 -t "%~dp1"
)
leandro at leandroleite dot info 26-Feb-2016 09:48
when I need to up a server for develop with rewrite, I use it:

// php -S 0.0.0.0:8080 -file webServer.php

// webServer.php
<?php
if (preg_match('/\.css|\.js|\.jpg|\.png|\.map$/', $_SERVER['REQUEST_URI'], $match)) {
   
$mimeTypes = [
       
'.css' => 'text/css',
       
'.js'  => 'application/javascript',
       
'.jpg' => 'image/jpg',
       
'.png' => 'image/png',
       
'.map' => 'application/json'
   
];
   
$path = __DIR__ . $_SERVER['REQUEST_URI'];
    if (
is_file($path)) {
       
header("Content-Type: {$mimeTypes[$match[0]]}");
        require
$path;
        exit;
    }
}
require_once
__DIR__.'/../app/bootstrap.php';
php dot chaska at xoxy dot net 21-Aug-2015 12:04
Note that to listen on a naked IPv6 address, you have to strangely enclose the address in so-called square brackets.  Example:

$ php -S [9990:116:a001:1900::1001]:8080 -t web/
simonbengt at gmail dot com 11-Mar-2015 07:59
My routing file looks like this:

if (file_exists($_SERVER["DOCUMENT_ROOT"] . $_SERVER["REQUEST_URI"])) {
    return false;
} else {
    require "index.php";
}

The reason is to support all static files.
tamas at bartatamas dot hu 13-Oct-2014 11:19
If your URI contains a dot, you'll lose the $_SERVER['PATH_INFO'] variable, when using the built-in webserver.
I wanted to write an API, and use .json ending in the URI-s, but then the framework's routing mechanism broke, and it took a lot of time to discover that the reason behind it was its router relying on $_SERVER['PATH_INFO'].

References:
https://bugs.php.net/bug.php?id=61286
jonathan at reinink dot ca 23-Jan-2014 12:26
In order to set project specific configuration options, simply add a php.ini file to your project, and then run the built-in server with this flag:

php -S localhost:8000 -c php.ini

This is especially helpful for settings that cannot be set at runtime (ini_set()).
Ivan Ferrer 11-Jul-2013 05:03
On Windows you may find useful to have a phpserver.bat file in shell:sendto with the folowing:
explorer http://localhost:8888
rem check if arg is file or dir
if exist "%~1\" (
  php -S localhost:8888 -t "%~1"
) else (
  php -S localhost:8888 -t "%~dp1"
)

then for fast web testing you only have to SendTo a file or folder to this bat and it will open your explorer and run the server.