Running PHP 5.4 Built-In Web Server Outside Localhost

Running PHP 5.4 built-in web server outside localhost

I did these tests on a Windows XP system, but should work the same on Linux as well by modifying the commands.

Run your PHP test server like this:

C:/php/php.exe -S 0.0.0.0:80
or
/usr/bin/php -S 0.0.0.0:80

0.0.0.0 will bind to all available IP addresses on the system.

On another machine on the network, I configured the hosts file to point to the internal IP of the system running PHP using a custom domain. This is not 127.0.0.1 as that refers to the local host, in my case I pointed my main PC to 192.168.88.247 which was the XP machine running PHP. Note the firewall should be disabled or set to allow traffic on port 80 on the machine running php.

I configured my router to port forward traffic from external port 80 to 192.168.88.247:80. Then using a hosts file on a PC from an external network, I configured the fake domain to point to my WAN IP. I was able to access the PHP web server externally.

That said, it is just a server for testing, so there may be unknown security risks opening it up to the outside world.

PHP 5.4 Built-in web server on home network

According to this answer you should be able to make PHP listen on all interfaces using a command-line parameter :

For Windows :

C:/php/php.exe -S 0.0.0.0:80

or, for GNU/Linux :

/usr/bin/php -S 0.0.0.0:80

Routing PHP 5.4+ Built-in Web Server like .htaccess

<?php
$_matches = array();

/**
* Initialize the rewrite environment.
*/
function initialize() {
set_environment($_SERVER['REQUEST_URI']);
}

/**
* Set important environment variables and re-parse the query string.
* @return boolean
*/
function finalize() {
if (defined('REWRITER_FINALIZED')) return false;

define('REWRITER_FINALIZED', true);

if (\is_file($_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME'])) {
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME'];
}

if (isset($_SERVER['QUERY_STRING'])) {
$_GET = [];
parse_str($_SERVER['QUERY_STRING'], $_GET);
}

$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];

return true;
}

/**
* Adjust the server environment variables to match a given URL.
* @param string $url
*/
function set_environment($url) {
$url = rtrim($url, '&?');
$request_uri = $script_name = $url;
$query_string = null;

if (strpos($url, '?') > 0) {
$script_name = substr($url, 0, strpos($url, '?'));
$query_string = substr($url, 1 + strpos($url, '?'));
}

$_SERVER['REQUEST_URI'] = $request_uri;
$_SERVER['SCRIPT_NAME'] = $script_name;
$_SERVER['QUERY_STRING'] = $query_string;
}

/**
* Parse regular expression matches. eg. $0 or $1
* @param string $url
* @return string
*/
function parse_matches($url) {
$replace = function($bit) {
global $matches;
return isset($matches[$bit[1]])
? $matches[$bit[1]]
: null;
};

return preg_replace_callback('/\$([0-9]+)/', $replace, $url);
}

/**
* Parse Apache style rewrite parameters. eg. %{QUERY_STRING}
* @param string $url
* @return string
*/
function parse_parameters($url) {
$replace = function($bit) {
return isset($_SERVER[$bit[1]])
? $_SERVER[$bit[1]]
: null;
};
return preg_replace_callback('/%\{([A-Z_+]+)\}/', $replace, $url);
}

/**
* Change the internal url to a different url.
* @param string $from Regular expression to match current url, or optional when used in conjunction with `test`.
* @param string $to URL to redirect to.
* @return boolean
*/
function rewrite($from, $to = null) {
if (defined('REWRITER_FINALIZED')) return false;

$url = $_SERVER['SCRIPT_NAME'];

if (isset($to)) {
$url = preg_replace($from, $to, $url);
} else {
$url = parse_matches($from);
}

set_environment(
parse_parameters($url)
);

return true;
}

/**
* Compare a regular expression against the current request, store the matches for later use.
* @return boolean
*/
function test($expression) {
global $matches;
if (defined('REWRITER_FINALIZED')) return false;
return 0 < (integer)preg_match($expression, $_SERVER['SCRIPT_NAME'], $matches);
}

initialize();

// Your rewrite rules here.
test('%/(.*)-(.*)\.htm$%') && rewrite('/?page=$1&sub=$2&%{QUERY_STRING}') && finalize();
test('%^([^/]*)\.htm$%') && rewrite('/?page=$0&%{QUERY_STRING}') && finalize();

echo "<pre>";
var_dump($_SERVER);
// include index.php or something

I've included a bunch of 'helper' functions which will make it easier to write your rewrite rules (borrowed here).

I cannot reach php built-in server running on a VM

You are binding your server to localhost. It is only listening on the localhost network interface. It won't be accessible outside of that machine.

Tell it to listen on your externally facing IP address instead.

Alternatively, tell it to listen on all network interfaces:

php -S 0.0.0.0:9889

Why PHP built-in web server should not be used on a public network?

From the documentation:

The web server runs only one single-threaded process, so PHP
applications will stall if a request is blocked.

Also, it won't restart by itself if for a reason or another the process crashes.

In a pinch, it's a good tool, but it's definitely not be used on a public network.

Fatfree routing with PHP built-in web server

Your issue is directly related to the way you're using the PHP built-in web server.

As stated in the PHP docs, here's how the server handles requests:

URI requests are served from the current working directory where PHP was started, unless the -t option is used to specify an explicit document root. If a URI request does not specify a file, then either index.php or index.html in the given directory are returned. If neither file exists, the lookup for index.php and index.html will be continued in the parent directory and so on until one is found or the document root has been reached. If an index.php or index.html is found, it is returned and $_SERVER['PATH_INFO'] is set to the trailing part of the URI. Otherwise a 404 response code is returned.

If a PHP file is given on the command line when the web server is started it is treated as a "router" script. The script is run at the start of each HTTP request. If this script returns FALSE, then the requested resource is returned as-is. Otherwise the script's output is returned to the browser.

That means that, by default (without a router script), the web server is doing a pretty good job for routing unexisting URIs to your document root index.php file.

In other words, provided your file structure is like:

lib/
base.php
template.php
etc.
public/
index.php

The following command is enough to start your server and dispatch the requests properly to the framework:

php -S 0.0.0.0:8090 -t public/

Or if you're running the command directly from the public/ folder:

cd public
php -S 0.0.0.0:8090

Beware that the working directory of your application depends on the folder from which you call the command. In order to leverage this value, I strongly advise you to add chdir(__DIR__); at the top of your public/index.php file. This way, all subsequent require calls will be relative to your public/ folder. For ex: $f3 = require('../lib/base.php');

Routing file-style URIs

The built-in server, by default, won't pass unexisting file URIs to your index.php, as stated in:

If a URI request does not specify a file, then either index.php or index.html in the given directory are returned

So if you plan to define some routes with dots, such as:

$f3->route('GET /brew.json','Brew->json');
$f3->route('GET /brew.html','Brew->html');

Then it won't work because PHP won't pass the request to index.php.

In that case, you need to call a custom router, such as the .htrouter.php you were trying to use. The only thing is that your .htrouter.php has obviously been designed for a different framework (F3 doesn't care about $_GET['url'] but cares about $_SERVER['SCRIPT_NAME'].

Here's an exemple of .htrouter.php that should work with F3:

// public directory definition
$public_dir=__DIR__.'/public';

// serve existing files as-is
if (file_exists($public_dir.$_SERVER['REQUEST_URI']))
return FALSE;

// patch SCRIPT_NAME and pass the request to index.php
$_SERVER['SCRIPT_NAME']='index.php';
require($public_dir.'/index.php');

NB: the $public_dir variable should be set accordingly to the location of the .htrouter.php file.

For example if you call:

php -S 0.0.0.0:8090 -t public/ .htrouter.php

it should be $public_dir=__DIR__.'/public'.

But if you call:

cd public
php -S 0.0.0.0:8090 .htrouter.php

it should be $public_dir=__DIR__.

PhpStorm give error when run say Built-in web server is only available since PHP 5.4

Just download latest version of XAMPP and install that. It will start working fine after reconfiguration.



Related Topics



Leave a reply



Submit