1 | <?php |
||
12 | namespace Lisachenko\Protocol\FCGI; |
||
13 | |||
14 | use Lisachenko\Protocol\FCGI; |
||
15 | |||
16 | /** |
||
17 | * Utility class to simplify parsing of FCGI protocol data. |
||
18 | * |
||
19 | * @author Alexander.Lisachenko |
||
20 | */ |
||
21 | class FrameParser |
||
22 | { |
||
23 | /** |
||
24 | * Mapping of constants to the classes |
||
25 | * @phpstan-var array<int, class-string> |
||
26 | */ |
||
27 | protected static array $classMapping = [ |
||
|
|||
28 | FCGI::BEGIN_REQUEST => FCGI\Record\BeginRequest::class, |
||
29 | FCGI::ABORT_REQUEST => FCGI\Record\AbortRequest::class, |
||
30 | FCGI::END_REQUEST => FCGI\Record\EndRequest::class, |
||
31 | FCGI::PARAMS => FCGI\Record\Params::class, |
||
32 | FCGI::STDIN => FCGI\Record\Stdin::class, |
||
33 | FCGI::STDOUT => FCGI\Record\Stdout::class, |
||
34 | FCGI::STDERR => FCGI\Record\Stderr::class, |
||
35 | FCGI::DATA => FCGI\Record\Data::class, |
||
36 | FCGI::GET_VALUES => FCGI\Record\GetValues::class, |
||
37 | FCGI::GET_VALUES_RESULT => FCGI\Record\GetValuesResult::class, |
||
38 | FCGI::UNKNOWN_TYPE => FCGI\Record\UnknownType::class, |
||
39 | ]; |
||
40 | |||
41 | /** |
||
42 | 1 | * Checks if the buffer contains a valid frame to parse |
|
43 | */ |
||
44 | 1 | public static function hasFrame(string $binaryBuffer): bool |
|
45 | 1 | { |
|
46 | $bufferLength = strlen($binaryBuffer); |
||
47 | if ($bufferLength < FCGI::HEADER_LEN) { |
||
48 | return false; |
||
49 | 1 | } |
|
50 | 1 | ||
51 | 1 | /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */ |
|
52 | $fastInfo = unpack(FCGI::HEADER_FORMAT, $binaryBuffer); |
||
53 | if ($fastInfo === false) { |
||
54 | 1 | throw new \RuntimeException('Can not unpack data from the binary buffer'); |
|
55 | } |
||
56 | if ($bufferLength < FCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) { |
||
57 | return false; |
||
58 | } |
||
59 | |||
60 | return true; |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | 1 | * Parses a frame from the binary buffer |
|
65 | * |
||
66 | 1 | * @return Record One of the corresponding FCGI record |
|
67 | 1 | */ |
|
68 | public static function parseFrame(string &$binaryBuffer): Record |
||
69 | { |
||
70 | 1 | $bufferLength = strlen($binaryBuffer); |
|
71 | 1 | if ($bufferLength < FCGI::HEADER_LEN) { |
|
72 | 1 | throw new \RuntimeException("Not enough data in the buffer to parse"); |
|
73 | } |
||
74 | /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */ |
||
75 | $recordHeader = unpack(FCGI::HEADER_FORMAT, $binaryBuffer); |
||
76 | if ($recordHeader === false) { |
||
77 | 1 | throw new \RuntimeException('Can not unpack data from the binary buffer'); |
|
78 | 1 | } |
|
79 | $recordType = $recordHeader['type']; |
||
80 | if (!isset(self::$classMapping[$recordType])) { |
||
81 | 1 | throw new \DomainException("Invalid FCGI record type {$recordType} received"); |
|
82 | 1 | } |
|
83 | |||
84 | 1 | /** @var Record $className */ |
|
85 | $className = self::$classMapping[$recordType]; |
||
86 | $record = $className::unpack($binaryBuffer); |
||
87 | |||
95 |