PHP code example of hiblaphp / http-server

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>'
);

return Response::plaintext('Hello');
return Response::json(['status' => 'created'], 201); // pretty-printed, unescaped slashes/unicode
return Response::html('<h1>Not Found</h1>', 404);
return Response::redirect('/login', 302);

return Response::file('/var/www/public/video.mp4', $request);

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);
}

$server->onError(function (\Throwable $e, Request $request) {
    return Response::json(['error' => $e->getMessage()], 500);
});

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()
    ->withMaxConnections(limit: 10000, pauseOnLimit: true) // Backpressure control
    ->withMaxBodySize(15 * 1024 * 1024)                    // 15MB max request body
    ->withHeaderLimits(maxSize: 8192, maxCount: 100)       // Prevent header bloat
    ->withMultipartLimits(maxFiles: 20, maxFields: 1000)   // Prevent hash collisions

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) 

HttpServer::create('0.0.0.0:443')
    ->withTls([
        'local_cert' => '/path/to/cert.pem',
        'local_pk'   => '/path/to/key.pem',
    ])

use Hibla\Socket\SocketServer;

$socket = new SocketServer('127.0.0.1:0'); // ephemeral port, useful in tests

HttpServer::create()
    ->withSocketServer($socket)
    ->onRequest(fn ($req) => Response::plaintext('ok'))
    ->start();

// Spawns 8 independent worker processes handling requests simultaneously
HttpServer::create('0.0.0.0:8000')
    ->withCluster(8)
    ->onRequest(...)
    ->start();

// 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');
});