DataFrame::class, self::FRAME_HEADERS => HeadersFrame::class, self::FRAME_PRIORITY => PriorityFrame::class, self::FRAME_SETTINGS => SettingsFrame::class, self::FRAME_PUSH_PROMISE => PushPromiseFrame::class, self::FRAME_PING => PingFrame::class, self::FRAME_GOAWAY => GoAwayFrame::class, self::FRAME_WINDOW_UPDATE => WindowUpdateFrame::class, self::FRAME_CONTINUATION => ContinuationFrame::class ]; /** @var int The associated condition is not a result of an error. For example, a GOAWAY might include this code to indicate graceful shutdown of a connection. */ const ERROR_NO_ERROR = 0x0; /** @var int The endpoint detected an unspecific protocol error. This error is for use when a more specific error code is not available. */ const ERROR_PROTOCOL_ERROR = 0x1; /** @var int The endpoint encountered an unexpected internal error. */ const ERROR_INTERNAL_ERROR = 0x2; /** @var int The endpoint detected that its peer violated the flow-control protocol. */ const ERROR_FLOW_CONTROL_ERROR = 0x3; /** @var int The endpoint sent a SETTINGS frame but did not receive a response in a timely manner. See Section 6.5.3 ("Settings Synchronization"). */ const ERROR_SETTINGS_TIMEOUT = 0x4; /** @var int The endpoint received a frame after a stream was half-closed. */ const ERROR_STREAM_CLOSED = 0x5; /** @var int The endpoint received a frame with an invalid size. */ const ERROR_FRAME_SIZE_ERROR = 0x6; /** @var int The endpoint refused the stream prior to performing any application processing (see Section 8.1.4 for details). */ const ERROR_REFUSED_STREAM = 0x7; /** @var int Used by the endpoint to indicate that the stream is no longer needed. */ const ERROR_CANCEL = 0x8; /** @var int The endpoint is unable to maintain the header compression context for the connection. */ const ERROR_COMPRESSION_ERROR = 0x9; /** @var int The connection established in response to aCONNECT request (Section 8.3) was reset or abnormally closed. */ const ERROR_CONNECT_ERROR = 0xa; /** @var int The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load. */ const ERROR_ENHANCE_YOUR_CALM = 0xb; /** @var int The underlying transport has properties that do not meet minimum security requirements (see Section 9.2). */ const ERROR_INADEQUATE_SECURITY = 0xc; /** @var int The endpoint requires that HTTP/1.1 be used instead of HTTP/2. */ const ERROR_HTTP_1_1_REQUIRED = 0xd; protected int $frameType; protected int $frameFlags; protected int $streamIdentifier; public function getFrameType(): int { return $this->frameType; } abstract public function toBinary(): string; abstract public function fromBinary(string $binary): void; public function encodeFrame(): string { $frame = ''; // TODO build header $payload = $this->toBinary(); $length = strlen($payload); $frame .= chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr(($length) & 0xFF) . chr($this->frameType) . chr($this->frameFlags) . chr(($this->streamIdentifier >> 24) & 0x7F) . chr(($this->streamIdentifier >> 16) & 0xFF) . chr(($this->streamIdentifier >> 8) & 0xFF) . chr(($this->streamIdentifier) & 0xFF) . $payload; return $frame; } public static function parseFrame(string &$data): Frame { $header = array_map('ord', str_split(substr($data,0,9))); $length = ($header[0] << 16) | ($header[1] << 8) | ($header[2]); $type = $header[3]; $flags = $header[4]; $stream = (($header[5] & 0x7F) << 24) | ($header[6] << 16) | ($header[7] << 8) | ($header[8]); if (!array_key_exists($type, self::$frameMap)) { // TODO handle this } /** @var Frame $frame */ $frame = new self::$frameMap[$type]; $frame->frameType = $type; $frame->frameFlags = $flags; $frame->streamIdentifier = $stream; // Grab the payload and parse it $payload = substr($data, 9, $length); $frame->fromBinary($payload); return $frame; } }