lisachenko/protocol-fcgi

Implementation of the FastCGI (FCGI) protocol in PHP

Maintainers

Package info

github.com/lisachenko/protocol-fcgi

pkg:composer/lisachenko/protocol-fcgi

Transparency log

Fund package maintenance!

lisachenko

Statistics

Installs: 105 026

Dependents: 3

Suggesters: 0

Stars: 126

Open Issues: 0

4.0.0 2026-07-30 17:17 UTC

README

CI PHPStan Badge Latest Version Total Downloads Daily Downloads Minimum PHP Version License

A zero-dependency, object-oriented implementation of the FastCGI 1.0 binary protocol for PHP.

FastCGI is the battle-tested protocol that web servers like nginx, Apache and Caddy use to talk to php-fpm — billions of requests flow through it every day. This library gives you the protocol itself as a clean, strictly-typed PHP API, so you can build your own high-performance FastCGI clients (talk to php-fpm directly, no web server in between) and servers (long-running PHP daemons that nginx can speak to natively).

âœĻ Key Features

  • ðŸ“Ķ Complete protocol coverage — all 11 FastCGI record types, including the management records (GET_VALUES, GET_VALUES_RESULT, UNKNOWN_TYPE)
  • 🌊 Streaming frame parser — feed partial socket reads into FrameParser::hasFrame() / parseFrame() and get fully-typed record objects out as soon as they are complete
  • 🔄 Byte-exact round-tripping — every record packs back to the exact wire bytes it was parsed from; the test suite is pinned to hex fixtures captured from real traffic
  • 📏 Automatic 8-byte padding — content alignment is handled for you, as the spec recommends
  • 🏷ïļ Full name-value pair encoding — including the 4-byte long form for names and values over 127 bytes
  • ðŸŽŊ Native enums for the protocol vocabulary — record types, roles and protocol statuses are backed enums (RecordType, Role, ProtocolStatus), so invalid wire values fail fast with a dedicated ProtocolException
  • ðŸŠķ Zero runtime dependencies — pure PHP, nothing but the language itself
  • 🔒 Strict types + PHPStan at the maximum level — the whole codebase (tests included) passes static analysis at the strictest setting

Requirements

  • PHP >= 8.4

Installation

composer require lisachenko/protocol-fcgi

Usage

The library implements both sides of the wire: use it to send FastCGI requests as a client, or to receive and answer them as a server. The full protocol specification is available at fast-cgi.github.io/spec.

FastCGI client: query php-fpm directly

<?php

use Lisachenko\Protocol\FCGI\FrameParser;
use Lisachenko\Protocol\FCGI\Record\BeginRequest;
use Lisachenko\Protocol\FCGI\Record\EndRequest;
use Lisachenko\Protocol\FCGI\Record\Params;
use Lisachenko\Protocol\FCGI\Record\Stdin;
use Lisachenko\Protocol\FCGI\Record\Stdout;
use Lisachenko\Protocol\FCGI\Role;

include 'vendor/autoload.php';

// Connect to the local php-fpm daemon directly
$phpSocket = fsockopen('127.0.0.1', 9001, $errorNumber, $errorString);

// Prepare the request: begin, pass parameters, then close the input stream.
// Empty Params and Stdin records mark the end of the corresponding stream.
$packet  = '';
$packet .= new BeginRequest(Role::Responder);
$packet .= new Params(['SCRIPT_FILENAME' => '/var/www/some_file.php']);
$packet .= new Params([]);
$packet .= new Stdin('');

fwrite($phpSocket, $packet);

// Read the response incrementally: the parser consumes complete frames
// from the buffer and leaves partial ones for the next read.
$buffer = '';
while ($partialData = fread($phpSocket, 4096)) {
    $buffer .= $partialData;
    while (FrameParser::hasFrame($buffer)) {
        $record = FrameParser::parseFrame($buffer);
        if ($record instanceof Stdout) {
            echo $record->getContentData();
        }
        if ($record instanceof EndRequest) {
            break 2; // response is complete
        }
    }
}

fclose($phpSocket);

FastCGI server: accept requests from a web server

<?php

use Lisachenko\Protocol\FCGI\FrameParser;

include 'vendor/autoload.php';

$server = stream_socket_server('tcp://127.0.0.1:9001', $errorNumber, $errorString);

// Accept one connection and parse everything the web server sends
$socket = stream_socket_accept($server);

$buffer = '';
while ($partialData = fread($socket, 4096)) {
    $buffer .= $partialData;
    while (FrameParser::hasFrame($buffer)) {
        $record = FrameParser::parseFrame($buffer);
        var_dump($record); // BeginRequest, Params, Stdin, ...
    }
}

// Answering (Stdout + EndRequest records) is up to your application

fclose($socket);
fclose($server);

Quality

composer test      # PHPUnit test suite
composer phpstan   # PHPStan static analysis
composer check     # both

License

Released under the MIT license.