The Server Runs Your Script
PHP turns web serving inside out compared to most languages: you do not write a server that runs forever — the web server runs your script, once per request. The script is the handler: superglobals like $_SERVER and $_GET hold the request, and header() plus echo build the response.
PHP ships its own web server for development: php -S host:port router.php answers every request by running that one script. The examples here really run it: each one writes its handler script to a temporary file, starts php -S on a free port as a child process, sends real HTTP requests to it, prints the answers, and stops the server — so every example runs and finishes by itself.
A First Handler
The smallest possible handler script writes a line of text. Everything an API does — JSON, status codes, routing — happens inside this same request-in, echo-out shape.
Hello from a handler
The handler script echoes one line; the client reads exactly that as the response body, with status 200 OK filled in automatically.
Output: PHP script
<?php
// The handler script: the web server runs this file once per request.
$router = <<<'PHP'
<?php
header("Content-Type: text/plain");
echo "hello from the server\n";
PHP;
$routerFile = sys_get_temp_dir() . "/pc-first-handler.php";
file_put_contents($routerFile, $router);
// A free port, then PHP's built-in web server as a child process.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$context = stream_context_create(["http" => ["ignore_errors" => true]]);
$body = file_get_contents("http://127.0.0.1:$port/", false, $context);
[, $status] = explode(" ", $http_response_header[0], 2);
echo "status: $status\n";
echo "body: $body";
proc_terminate($server);
proc_close($server);
unlink($routerFile);
Answering with JSON
An API handler does three things: set the Content-Type header, choose a status code, and encode the data — header() first, then echo json_encode($value).
Order matters: headers must be sent before the first byte of the body.
A JSON endpoint
The handler encodes an array straight into the response. The client receives valid JSON and the right content type — this is the skeleton of every endpoint in the rest of this group.
Output: PHP script
<?php
// An API handler: header first, then the encoded body.
$router = <<<'PHP'
<?php
header("Content-Type: application/json");
echo json_encode(["id" => 1, "name" => "Ana"]) . "\n";
PHP;
$routerFile = sys_get_temp_dir() . "/pc-json-handler.php";
file_put_contents($routerFile, $router);
// A free port, then PHP's built-in web server as a child process.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$context = stream_context_create(["http" => ["ignore_errors" => true]]);
$body = file_get_contents("http://127.0.0.1:$port/user", false, $context);
[, $status] = explode(" ", $http_response_header[0], 2);
$type = "";
foreach ($http_response_header as $headerLine) {
if (stripos($headerLine, "Content-Type:") === 0) {
$type = trim(substr($headerLine, 13));
}
}
echo "status: $status\n";
echo "type: $type\n";
echo "body: $body";
proc_terminate($server);
proc_close($server);
unlink($routerFile);