1. Go to this page and download the library: Download hiblaphp/http-server library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
hiblaphp / http-server example snippets
use Hibla\HttpServer\HttpServer;
use Hibla\HttpServer\Message\Request;
use Hibla\HttpServer\Message\Response;
use function Hibla\await;
HttpServer::create('127.0.0.1:8000')
->withMaxBodySize(10 * 1024 * 1024) // 10MB
->onRequest(function (Request $request) {
if ($request->method === 'POST' && $request->uri === '/api/echo') {
// Await safely inside the handler; the server runs it in an async Fiber!
$data = await($request->getJson());
return Response::json(['you_sent' => $data]);
}
return Response::plaintext("Hello from the Server! You requested: {$request->uri}");
})
->start();
// 1. Buffers the streaming body into a string asynchronously
$rawBodyString = await($request->getBufferedBody());
// 2. Buffers the stream and parses it as a JSON array on-the-fly
$decodedJson = await($request->getJson());
// 3. Streams and parses multipart/form-data asynchronously
$form = await($request->getParsedBody());
$server->onRequest(function (Request $request) {
// FATAL: This completely halts the PHP process thread.
// No other users can connect or receive data for 2 seconds!
sleep(2);
// FATAL: This blocks the thread waiting for network I/O.
$data = file_get_contents('https://api.example.com/data');
// FATAL: Standard PDO blocks the thread while waiting for the database.
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
return Response::plaintext($data);
});
$server->onRequest(function (Request $request) {
// SAFE: This suspends only this specific request's Fiber.
// The server instantly switches to serving other connected users.
await(\Hibla\delay(2.0));
// SAFE: Use the async i/o from the Hibla ecosystem.
$response = await(\Hibla\HttpClient\Http::get('https://api.example.com/data'));
$database = await(\Hibla\QueryBuilder\DB::table("users")->get());
return Response::json([
$response->body(),
'data' => $database
]);
});
use Hibla\HttpServer\HttpServer;
use Hibla\HttpServer\Message\Request;
use Hibla\HttpServer\Message\Response;
use function Hibla\await;
use function Hibla\delay;
use function Hibla\inFiber;
// Deeply nested helper function
function taskInsideNestedCallback(): void {
// Returns true because the server started the Fiber at the root onRequest handler
echo inFiber()
? "The deeply nested task is in a Fiber context\n"
: "The deeply nested task is not in a Fiber context\n";
}
// Plain, standard PHP function with no "color"
function main(): bool {
await(delay(0.05));
taskInsideNestedCallback();
return inFiber();
}
$server = HttpServer::create('127.0.0.1:8080')
->onRequest(function (Request $request) {
if (main()) {
echo "The main orchestrator function is in a Fiber\n";
}
return Response::plaintext("Hello");
});
await(
Promise::map(["hello", "world"], function ($item) {
// DANGER: Promise::map executes this closure later on {main}, outside a Fiber!
// Calling await() here in Strict Mode will instantly throw an InvalidContextException
// pointing to the UserController.php file.
await(delay(0.01));
return strtoupper($item);
})
);
use function Hibla\asyncFn;
await(
Promise::map(["hello", "world"], asyncFn(function ($item) {
// SAFE: asyncFn() spawns a new dedicated Fiber for this closure.
// await() will safely suspend this specific Fiber without blocking the server.
await(delay(0.01));
return strtoupper($item);
}))
);
use function Hibla\async;
use function Hibla\await;
use Hibla\Promise\Promise;
use Hibla\QueryBuilder\DB;
function fetchUserData(int $id): array
{
$userData = await(DB::table("users")->where('id', '=', $id)->get());
}
$server->onRequest(function (Request $request) {
// Wrap plain functions in async() only when you need to run them concurrently!
$user1Promise = async(fn() => fetchUserData(42));
$user2Promise = async(fn() => fetchUserData(99));
// Resolve both concurrently
[$user1, $user2] = await(Promise::all([$user1Promise, $user2Promise]));
return Response::json([$user1, $user2]);
});
$method = $request->method; // e.g. "POST"
$uri = $request->uri; // e.g. "/api/v1/users?active=true"
$version = $request->protocolVersion; // e.g. "1.1"
// Retrieve headers case-insensitively
$token = $request->getHeaderLine('Authorization');
// Buffers the entire body into memory, rejecting with PayloadTooLargeException
// if it exceeds the server's max body size.
$raw = await($request->getBufferedBody());
// You can also override the global limit on a per-request basis
$largeRaw = await($request->getBufferedBody(maxBytes: 50 * 1024 * 1024));
// Buffers and json_decode()s the body; throws MessageParsingException on invalid JSON.
$data = await($request->getJson());
use Hibla\HttpServer\Message\MultipartForm;
use Hibla\HttpServer\Message\UploadedFile;
$form = await($request->getParsedBody());
// 1. Get standard form fields
$username = $form->get('username');
// 2. Access uploaded files
$file = $form->getFile('avatar'); // returns ?UploadedFile
if ($file instanceof UploadedFile) {
echo $file->clientFilename; // e.g. "profile_pic.jpg" (path-stripped, see hardening notes)
echo $file->clientMediaType; // e.g. "image/jpeg" (client-supplied, unverified)
echo $file->size; // Size in bytes, counted while streaming to disk
// Move the file asynchronously using pure stream pipes (non-blocking)
await($file->moveTo('/var/www/uploads/' . $file->clientFilename));
}
use Hibla\Stream\Interfaces\PromiseReadableStreamInterface;
await($request->streamMultipart(
onFile: function (string $name, string $filename, string $mime, PromiseReadableStreamInterface $fileStream) {
echo "Streaming file '{$filename}' directly to cloud storage...\n";
// Read chunks asynchronously directly from the TCP socket as they land!
// This callback is executed in an isolated Fiber, so await is safe.
while (($chunk = await($fileStream->readAsync(8192))) !== null) {
// Write directly to your S3 or Cloud stream
await($s3UploadStream->writeAsync($chunk));
}
echo "Finished streaming {$filename}.\n";
},
onField: function (string $name, string $value) {
// Standard form fields are delivered here as soon as they are parsed
echo "Received field: {$name} = {$value}\n";
}
));
$form->get('tags'); // first value only, or null
$form->getAll('tags'); // every value submitted for "tags", in order
$form->getFile('photos'); // first uploaded file for "photos", or null
$form->getFiles('photos');// every uploaded file for "photos", in order
$form->all(); // array<string, list<string>> of every field
use Hibla\HttpServer\Message\Response;
// Explicit response creation
$response = new Response(
statusCode: 200,
headers: ['Content-Type' => 'text/html'],
body: '<h1>Welcome</h1>'
);
use Hibla\HttpServer\Message\SseStream;
return Response::sse(function (SseStream $stream) {
while ($stream->isReadable()) {
$data = json_encode(['metrics' => getSystemMetrics()]);
// Push the formatted SSE frame onto the wire
$stream->send($data, event: 'metrics_update');
// Sleep for 1 second asynchronously without blocking the event loop!
await(\Hibla\delay(1.0));
}
});
return Response::upgrade(
status: 101,
headers: ['Upgrade' => 'websocket', 'Connection' => 'Upgrade'],
onUpgrade: function ($connection, string $trailingBytes) {
// You now own the raw Hibla\Socket\Interfaces\ConnectionInterface socket!
$connection->write("Switched to WebSocket protocol.\n");
$connection->on('data', function ($chunk) use ($connection) {
$connection->write("Echo: " . $chunk);
});
}
);
use Hibla\HttpServer\Exceptions\PayloadTooLargeException;
use Hibla\HttpServer\Exceptions\MessageParsingException;
try {
$data = await($request->getJson());
} catch (PayloadTooLargeException $e) {
return Response::plaintext('Body too large', 413);
} catch (MessageParsingException $e) {
return Response::plaintext('Invalid JSON', 400);
}
use Hibla\Cancellation\CancellationTokenSource;
use Hibla\Promise\Exceptions\CancelledException;
$server->onRequest(function (Request $request) {
// 1. Create a Cancellation Token Source
$cts = new CancellationTokenSource();
// 2. If the user closes their browser tab, trigger the cancellation!
$request->onClientDisconnect(function () use ($cts) {
$cts->cancel();
});
try {
// 3. Pass the token down to your async tasks.
// If $cts->cancel() fires, the fetch operation instantly aborts.
$data = await(fetchAnalyticsDataAsync($cts->token));
return Response::json($data);
} catch (CancelledException $e) {
// The promise was cancelled cleanly.
// Returning null instructs the server not to attempt sending a response.
return null;
}
});
HttpServer::create()
// Drop if headers take > 5s (Default: null or disabled)
->withHeaderTimeout(5.0)
// Drop if 10s pass between body chunks (Default: null or disabled)
->withBodyTimeout(10.0)
// Hard 60s absolute limit for the whole request (Default: null or disabled)
->withRequestTimeout(60.0)
HttpServer::create()
// Max time to drain requests on SIGTERM (Default: 15.0 seconds)
->withGracefulShutdownTimeout(15.0)
HttpServer::create()
// Close idle connections after 5s (Default: null or disabled)
->withKeepAliveTimeout(5.0)
// Force reconnect after 100 requests (Default: null or unlimited)
->withKeepAliveMaxRequests(100)
// Set the HTTP/1.1 Pipelining depth queue (Default: 128)
->withMaxConcurrentRequestsPerConnection(128)
// Executed in the MASTER PROCESS
$logger = new FileLogger('/var/log/app.log');
HttpServer::create('127.0.0.1:8000')
->withCluster(4)
->onError(function (\Throwable $e, Request $request) use ($logger) {
// ERROR: The Parallel library cannot serialize the $logger file resource
// to send it to the child workers. This will throw a TaskPayloadException.
$logger->log($e->getMessage());
})
->start();
HttpServer::create('127.0.0.1:8000')
->withCluster(4)
->onStart(function () {
// This runs INSIDE the child worker right after it boots.
// It is perfectly safe to open Database/File resources here.
global $workerLogger;
$workerLogger = new FileLogger('/var/log/app.log');
})
->onError(function (\Throwable $e, Request $request) {
// Safe to serialize: captures no external scope
global $workerLogger;
$workerLogger->log($e->getMessage());
return Response::plaintext('Internal Error', 500);
})
->start();
use Hibla\HttpServer\ClusterOptions;
$options = ClusterOptions::make()
->withWorkerMemoryLimit('256M')
->withWorkerRestartLimit(10) // Prevent fork-bomb crash loops
->withClusterBootstrap('/path/to/bootstrap') // Preload bootstrap code like DI or service providers
->onWorkerMessage(function ($message) {
// Receive messages emitted by workers to the Master process
echo "Worker {$message->pid} says: {$message->data}\n";
});
HttpServer::create()
->withCluster(4, $options)
->onRequest(function(Request $request) {
\Hibla\emit('I just served a request!'); // Send message to Master via IPC
return Response::plaintext('OK');
})
->start();
use Hibla\HttpServer\Interfaces\ProtocolHandlerInterface;
use Hibla\HttpServer\Message\Request;
use Hibla\HttpServer\Message\Response;
$server->onRequest(function (Request $request, ProtocolHandlerInterface $protocol) {
// 1. Inspect the underlying socket connection directly
$socket = $protocol->connection;
$remoteIp = $socket->getRemoteAddress();
// 2. Check active pipeline concurrency on this specific socket
$concurrentRequestsOnSocket = $protocol->activeRequestsCount;
// 3. Write responses with explicit event callbacks
$protocol->writeResponse(Response::plaintext('Direct Write'), function () {
// This callback is executed the exact millisecond the bytes leave the OS buffer!
echo "Response fully transmitted to client.\n";
});
});
$server->onRequest(function (Request $request, ProtocolHandlerInterface $protocol) {
$socket = $protocol->connection;
// Stop reading any new TCP packets from this client!
// The client's OS will buffer data locally (TCP Window saturation)
$socket->pause();
Hibla\async(function () use ($socket, $request) {
// Process a slow, database i/o task asynchronously
await(slowDatabaseWrite(await($request->getBufferedBody())));
// We are ready for more data. Tell the kernel to resume reading!
$socket->resume();
});
return Response::plaintext('Processed');
});
$server->onRequest(function (Request $request, ProtocolHandlerInterface $protocol) {
if ($request->getHeaderLine('Upgrade') === 'my-custom-protocol') {
// Send the HTTP protocol switch response
$protocol->writeResponse(new Response(101, [
'Upgrade' => 'my-custom-protocol',
'Connection' => 'Upgrade'
]));
// Hijack and detach!
$rawSocket = $protocol->connection;
$unparsedBytes = $protocol->detach(); // Cleans up and returns trailing bytes
// The HTTP server has completely forgotten about this socket.
// You are now writing raw TCP data:
if ($unparsedBytes !== '') {
processCustomFraming($unparsedBytes);
}
$rawSocket->on('data', function (string $chunk) use ($rawSocket) {
$rawSocket->write("Echo: " . $chunk);
});
return; // Return null so the server knows not to try to send a response
}
return Response::plaintext('Standard HTTP');
});
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.