🔖

【PHP】3・4バイト列と数値の相互変換と HTTP/2 フレームヘッダーの生成

2024/08/01に公開

HTTP/2 フレームのヘッダー(9バイト)を扱う場合、長さの部分(3バイト)とストリーム id の部分に関してバイト列と数値を相互変換するための関数を用意する必要がある。

<?php
// https://gist.github.com/masakielastic/8e93e3ad771be0cef0081f23bd9b35a2

var_dump(
    0x0 === binToInt24("\x00\x00\x00"),
    0x0 === binToInt32("\x00\x00\x00\x00"),
    "\x00\x00\x00" === int24ToBin(0x0),
    "\x00\x00\x00\x00"=== int32ToBin(0x0)
);

$settings = implode([
    "length" =>  "\x00\x00\x00",
    "type" => "\x04",
    "flag" => "\x00",
    "streamId" => "\x00\x00\x00\x00",
    "payload" => ""
]);

$settings2 = implode([
   "length" => "\x00\x00\x0C",
   "type"   => "\x04",
   "flag" => "\x00",
   "streamId" => "\x00\x00\x00\x00",
   "payload" => "\x00\x02"."\x00\x00\x00\x00".
                "\x00\x03"."\x00\x00\x00\x64"
]);

var_dump(
    $settings === createFrameHeader(0x0, 0x4, 0x0, 0x0),
    $settings === implode(frameHeaderToArray($settings)),
    $settings2 === implode(frameToArray($settings2))
);

// https://github.com/dart-lang/http2/blob/master/lib/src/byte_utils.dart

function binToInt16(string $bytes): int
{
    return (ord($bytes[0]) << 8) | ord($bytes[1]);
}

function binToInt24(string $bytes): int
{
    return (ord($bytes[0]) << 16) |
           (ord($bytes[1]) << 8) |
            ord($bytes[2]);
}

function binToInt32(string $bytes): int
{
    return (ord($bytes[0]) << 24) |
           (ord($bytes[1]) << 16) |
           (ord($bytes[2]) <<  8) |
            ord($bytes[3]);
}

function int16ToBin(int $value): string
{
    return chr(($value >> 8) & 0xff).chr($value & 0xff);
}

function int24ToBin(int $value): string
{
    return chr(($value >> 16) & 0xff).
           chr(($value >>  8) & 0xff).
           chr($value & 0xff);
}

function int32ToBin(int $value): string
{
    return chr(($value >> 24) & 0xff).
           chr(($value >> 16) & 0xff).
           chr(($value >>  8) & 0xff).
           chr($value & 0xff);
}

function createFrameHeader(int $length, int $type, int $flag, int $streamId): string
{
    return int24ToBin($length).chr($type).chr($flag).int32ToBin($streamId);
}

function frameHeaderToArray(string $bytes): array
{
    return [
        'length' => substr($bytes, 0, 3),
        'type' => substr($bytes, 3, 1),
        'flag' => substr($bytes, 4, 1),
        'streamId' => substr($bytes, 5, 4)
    ];
}

function frameToArray(string $bytes): array
{
    $ret = frameHeaderToArray($bytes);
    $ret['payload'] = substr($bytes, 9);
    return $ret;
}

Discussion