VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Using PHP’s built-in web server in your test suites

PHP

As of PHP-5.4.0 the CLI SAPI provides a built-in web server. The web server is designed for development purposes, and serves requests sequentially. This web server can come in really handy when the need for an httpd arises during (integration) tests.

In this post I’ll use PHPUnit as the testing framework, and I’ll show you how to start the web server during the bootstrap process, and how to shut it down when the test suite is finished.

PHPUnit lets you specify a bootstrap file that is executed before the tests. This bootstrap file will contain the code needed to start and stop the web server. First, specify a bootstrap file in PHPUnit’s XML configuration file:

Show code
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php">
  <testsuites>
    <testsuite name="Name of the test suite">
      <directory>./tests</directory>
    </testsuite>
  </testsuites>
  <php>
    <const name="WEB_SERVER_HOST" value="localhost" />
    <const name="WEB_SERVER_PORT" value="1349" />
    <const name="WEB_SERVER_DOCROOT" value="./public" />
  </php>
</phpunit>

The three constants we define holds the host and the port that the server will listen on (localhost:1349) and the path to the document root (./public). These constants will be used in our bootstrap.php script when starting up the web server.

Now, on to the bootstrap script:

Show code
<?php
// Command that starts the built-in web server
$command = sprintf(
    'php -S %s:%d -t %s >/dev/null 2>&1 & echo $!',
    WEB_SERVER_HOST,
    WEB_SERVER_PORT,
    WEB_SERVER_DOCROOT
);

// Execute the command and store the process ID
$output = array(); 
exec($command, $output);
$pid = (int) $output[0];

echo sprintf(
    '%s - Web server started on %s:%d with PID %d', 
    date('r'),
    WEB_SERVER_HOST, 
    WEB_SERVER_PORT, 
    $pid
) . PHP_EOL;

// Kill the web server when the process ends
register_shutdown_function(function() use ($pid) {
    echo sprintf('%s - Killing process with ID %d', date('r'), $pid) . PHP_EOL;
    exec('kill ' . $pid);
});

// More bootstrap code

With this bootstrap script the web server will be started before the tests execute, and when the test suite is finished the shutdown function will kill the web server process.

If you execute PHPUnit with the above XML configuration and the bootstrap file you should see something like this:

Show code
Fri, 19 Jul 2013 11:09:09 +0200 - Web server started on localhost (port 1349) with PID 9877
PHPUnit 3.7.22 by Sebastian Bergmann.

Configuration read from /path/to/phpunit.xml.dist



Time: 0 seconds, Memory: 1.75Mb

No tests executed!
Fri, 19 Jul 2013 11:09:09 +0200 - Killing process with ID 9877

SUCCESS!

If you have any other use cases for the built-in web server, feel free to leave a comment.

Senior developer at VG. Coder of code, drinker/brewer of beer and listener of metal/punk/hc. @cogocogo | @BeerNorway | www.beernorway.com


11 comments

  • Jesus A. Domingo

    Thanks for posting this. One thing I had to do when I used this, you have to give the server some time to start up before you fire your tests. In my case, I had to put sleep(1) after this routine or else, the first few tests would fail.


  • Дайджест интересных новостей и материалов из мира PHP за последние две недели (15.07.2013 — 28.07.2013) - Juds

    [...] Использование встроенного веб-сервера PHP в ваших теста... — Начиная с версии 5.4 PHP поставляется со встроенным веб-сервером. Автор поста демонстрирует способ конфигурации PHPUnit для запуска веб сервера перед выполнением набора тестов, и завершением его работы после окончания тестирования. [...]


  • Using PHP’s built-in web server in Behat tests – VG Tech

    […] weeks back I wrote a post showing you how to use PHP’s built in web server in PHPUnit. This post will show you how to do the same for Behat when running your acceptance […]


  • PHP Digest №1 ( 11.08.2013 — 25.08.2013 ) | FASIGN Blog

    […] Webserver in PHP — Vor einiger Zeit hat der Author schonmal darüber geschrieben wie man den eingebauten Webserver in PHP (≥ 5.4) mit PHPUnit nutzt, nun ist Behat an der […]


  • Ryan Brodkin

    Thanks! This is really helpful.

    I had to add a sleep(1) to the bottom of my bootstrap to get this working for me or else the server wasn't fully started before the first test executed, but other than that it's awesome!


  • Paul

    Thanks for this, I found it incredibly helpful when getting started with PHPUnit.

    One thing I would add is that you might need to add a call to date_default_timezone_set() if that hasn't been set elsewhere (e.g. in php.ini or another include file) as you're calling date() in several places.


  • Ozh

    For the record, this doesn't work on Windows, where PHP fails to start the process in the background


  • Starli0n

    Adding Windows support

    ````
    function phpServe()
    {
    // OS detection
    $isWindows = stristr(php_uname('s'), 'Windows') !== FALSE;

    // Command that starts the built-in web server
    if ($isWindows) {
    $command = sprintf(
    'wmic process call create "php -S %s:%d -t %s" | find "ProcessId"',
    WEB_SERVER_HOST,
    WEB_SERVER_PORT,
    __DIR__ . '/../' . WEB_SERVER_DOCROOT
    );
    $killCommand = 'taskkill /f /pid ';
    } else {
    $command = sprintf(
    'php -S %s:%d -t %s >/dev/null 2>&1 & echo $!',
    WEB_SERVER_HOST,
    WEB_SERVER_PORT,
    WEB_SERVER_DOCROOT
    );
    $killCommand = 'kill ';
    }

    // Execute the command and store the process ID
    $output = array();
    echo sprintf('Starting server...') . PHP_EOL;
    echo sprintf(' Current directory: %s', getcwd()) . PHP_EOL;
    echo sprintf(' %s', $command);
    exec($command, $output);

    // Get PID
    if ($isWindows) {
    $pid = explode('=', $output[0]);
    $pid = str_replace(' ', '', $pid[1]);
    $pid = str_replace(';', '', $pid);
    } else {
    $pid = (int) $output[0];
    }

    // Log
    echo sprintf(
    ' %s - Web server started on %s:%d with PID %d',
    date('r'),
    WEB_SERVER_HOST,
    WEB_SERVER_PORT,
    $pid
    ) . PHP_EOL;

    // Kill the web server when the process ends
    register_shutdown_function(function() use ($killCommand, $pid) {
    echo PHP_EOL . sprintf('Stopping server...') . PHP_EOL;
    echo sprintf(' %s - Killing process with ID %d', date('r'), $pid) . PHP_EOL;
    exec($killCommand . $pid);
    });
    }
    ````


  • Kevin Gilbert

    I had to test code related to output buffering (ob_start) and it was simply impossible to test.

    I'm now creating a webserver with docroot located inside my "tests" folder and its working great !

    Thanks !


Leave your comment