Guidance for AI coding agents (and humans) working on this repository.
lisachenko/protocol-fcgi is a zero-dependency, object-oriented implementation of the
FastCGI 1.0 binary protocol for PHP. It provides
low-level building blocks (records and a frame parser) for writing FastCGI clients
(e.g. talking to php-fpm directly) and FastCGI servers/daemons.
- Package name:
lisachenko/protocol-fcgi - PSR-4 namespace:
Lisachenko\Protocol\→src/ - Runtime dependencies: none (only PHP itself)
| Path | Purpose |
|---|---|
src/FCGI.php |
Protocol-level constants (header length/format, version, flags) |
src/FCGI/RecordType.php, Role.php, ProtocolStatus.php |
Int-backed enums for the protocol vocabulary; RecordType::recordClass() maps types to record classes |
src/FCGI/ProtocolException.php |
Thrown on malformed/truncated buffers and invalid wire values |
src/FCGI/Record.php |
Base record: header pack/unpack, payload handling, __toString() emits wire bytes |
src/FCGI/FrameParser.php |
Turns a binary buffer into typed Record instances (hasFrame() / parseFrame()) |
src/FCGI/Record/*.php |
One class per FCGI record type (BeginRequest, EndRequest, Params, Stdin, Stdout, Stderr, Data, AbortRequest, GetValues, GetValuesResult, UnknownType) |
tests/ |
PHPUnit tests mirroring src/, driven by hex fixtures captured from real traffic |
composer install # install dev dependencies
composer test # run the PHPUnit test suite (vendor/bin/phpunit)
composer phpstan # run PHPStan static analysis (vendor/bin/phpstan analyse)
composer check # run bothBoth PHPStan and PHPUnit must pass before pushing.
- Conventional Commits for every commit message:
feat:,fix:,chore:,docs:,refactor:,perf:,ci:,test:. Use!(e.g.feat!:) for breaking changes. - Branch naming:
<type>/<kebab-description>, e.g.feat/websocket-records,fix/padding-overflow,docs/update-readme. - Every PHP file starts with
declare(strict_types=1);. - Static analysis runs at the level configured in
phpstan.neonover bothsrc/andtests/— do not add suppressions or baselines; fix the root cause instead. - Tests are plain PHPUnit
TestCaseclasses withtestXxx(): voidmethods. - Tests intentionally share the production namespace (
Lisachenko\Protocol\...) via theautoload-devmapping — do not "fix" this.
This library implements a binary network protocol. Byte layouts must never change:
- Every change to a record class needs a pack/unpack round-trip test asserting
byte-exact output against a captured hex fixture (see the existing
$rawMessagefixtures intests/, taken from Wireshark captures of real FastCGI traffic). - Records are padded to an 8-byte boundary; padding is computed in
Record::setContentData(). FrameParser::parseFrame()mutates the given buffer by reference, consuming the parsed frame.- Unpacking creates records without invoking constructors (hydration), so the raw
wire bytes are preserved exactly even for legal-but-non-canonical peer encodings.
Record-specific fields are hydrated by the protected instance method
unpackPayload(); keep class-level property defaults (not constructor promotion) for any field that must survive a zero-length payload (seeParams::$values). Paramsname-value pairs use variable-length encoding: 1-byte lengths for < 128, 4-byte lengths (high bit set) otherwise.
- Keep PRs focused: one concern per PR.
- Update tests alongside code; update
CHANGELOG.mdfor user-visible changes. - CI (GitHub Actions) runs PHPUnit across the supported PHP matrix plus PHPStan — a green build is required.