✅
以下是一个纯 PHP 编写的通用 Protobuf 二进制生成器,支持:
-
varint
-
fixed32
-
fixed64
-
length-delimited
(如字符串、嵌套 message) -
嵌套结构 (
nested
) -
多字段 repeated
✅ 封装器代码(可直接用)
<?phpfunction writeVarint($value) {$result = '';while (true) {if (($value & ~0x7F) === 0) {$result .= chr($value);break;} else {$result .= chr(($value & 0x7F) | 0x80);$value >>= 7;}}return $result;
}function writeFieldHeader($fieldNum, $wireType) {return writeVarint(($fieldNum << 3) | $wireType);
}function writeVarintField($fieldNum, $value) {return writeFieldHeader($fieldNum, 0) . writeVarint($value);
}function writeFixed32Field($fieldNum, $hex) {if (strlen($hex) !== 8) throw new Exception("fixed32 hex 必须为 8 位");return writeFieldHeader($fieldNum, 5) . hex2bin($hex);
}function writeFixed64Field($fieldNum, $hex) {if (strlen($hex) !== 16) throw new Exception("fixed64 hex 必须为 16 位");return writeFieldHeader($fieldNum, 1) . hex2bin($hex);
}function writeLengthDelimitedField($fieldNum, $value) {return writeFieldHeader($fieldNum, 2) . writeVarint(strlen($value)) . $value;
}function buildMessage($fields) {$message = '';foreach ($fields as $field) {$fieldNum = $field['field'];$type = $field['type'];$value = $field['value'];switch ($type) {case 'varint':$message .= writeVarintField($fieldNum, $value);break;case 'fixed32':$message .= writeFixed32Field($fieldNum, $value);break;case 'fixed64':$message .= writeFixed64Field($fieldNum, $value);break;case 'string':$message .= writeLengthDelimitedField($fieldNum, $value);break;case 'bytes':$message .= writeLengthDelimitedField($fieldNum, hex2bin($value));break;case 'nested':$nested = buildMessage($value); // 递归生成嵌套$message .= writeLengthDelimitedField($fieldNum, $nested);break;default:throw new Exception("不支持的字段类型: $type");}}return $message;
}
示例调用方式
$protobuf = buildMessage([['field' => 1, 'type' => 'varint', 'value' => 123],['field' => 2, 'type' => 'fixed64', 'value' => '0011223344556677'],['field' => 3, 'type' => 'string', 'value' => 'hello'],['field' => 4, 'type' => 'bytes', 'value' => 'deadbeef'],['field' => 5, 'type' => 'nested', 'value' => [['field' => 1, 'type' => 'string', 'value' => 'inner']]]
]);echo base64_encode($protobuf);
以下是一个 JSON 转 Protobuf 结构数组 的示例转换器,用于搭配你已有的 buildMessage()
使用:
✅ 1. JSON → PHP 数组结构转换器
🔧 示例 JSON 格式
[{ "field": 1, "type": "varint", "value": 123 },{ "field": 2, "type": "fixed64", "value": "0011223344556677" },{ "field": 3, "type": "string", "value": "hello" },{"field": 4,"type": "nested","value": [{ "field": 1, "type": "string", "value": "inner" }]}
]
✅ 加载并构造的 PHP 代码
function buildFromJson($jsonStr) {$decoded = json_decode($jsonStr, true);if (!is_array($decoded)) {throw new Exception("无效 JSON 格式");}return buildMessage($decoded);
}
✅ 完整示例组合用法:
include 'protobuf_builder.php'; // 假设你把封装器写到这个文件里$json = '[{ "field": 1, "type": "varint", "value": 123 },{ "field": 2, "type": "fixed64", "value": "0011223344556677" },{ "field": 3, "type": "string", "value": "hello" },{"field": 4,"type": "nested","value": [{ "field": 1, "type": "string", "value": "inner" }]}
]';$protobuf = buildFromJson($json);
echo base64_encode($protobuf);
✅ 输出说明
这段代码会输出一段 符合 JSON 结构的 Protobuf 二进制串,你可用于发送、调试、对比:
<?php
// CGQQl1oaBTEuMi4xInxleUpOWVhKclNXUWlPakFzSWxSNWNHVWlPakVzSWtGd2NFbGtJam96T0RReE5UZ3NJbFZ6WlhKSlpDSTZOelV3T1RZNE5qWTJOVEk0T1RVMk5USTBNeXdpVkdsdFpYTjBZVzF3SWpveE56VXdPVFUxTmpVME56STFmUT09KAMwADoOYzg5ZThkZjptYXN0ZXJCigaiBoYGCigwOjE6MTg5Mzc3Mzc0NzE3MTc5OTo3NTA5Njg2NjY1Mjg5NTY1MjQzEAEYi4SF1KeN76loIhF7InRleHQiOiLlp5Dlp5AifSo5ChNhOmNsdWVfYjJjX3RpbWVfbG9nEiJ7ImIyY19mZV9zZW5kX3RpbWUiOjE3NTA5NjA4NDc0NjJ9KisKGmE6aW1fZ2VuZXJhbF9zZW5kX21zZ190aW1lEg0xNzUwOTYwODQ3NDYyKiIKEmE6c2VuZF90aW1lX3NvdXJjZRIMY29tbWVyY2VfcHJpKhMKDmNzOm1zZ19jaGFubmVsEgExKhEKDGNzOmNvbnZfbW9kZRIBMSoUCg9jczphY2NvdW50X3R5cGUSATEqDwoKY3M6dG9vbF9pZBIBMCofChNsZWFkc19pbV9jaGF0X3NjZW5lEghjb21tZXJjZSofChNsZWFkc19pbV9tc2dfc2NoZW1hEghjb21tZXJjZSorChdhOnNhaXlhbl9pbV9zZW5kX21ldGhvZBIQQ3NfTGVhZHNQbGF0Zm9ybSoYChRsZWFkc19pbV9iaXpfY2hhdF9pZBIAKicKFWxlYWRzX2ltX21zZ19wbGF0Zm9ybRIObGVhZHNfcGxhdGZvcm0qGAoTbGVhZHNfcGNfZGlyZWN0X21zZxIBMSoVChFzOm1lbnRpb25lZF91c2VycxIAKjsKE3M6Y2xpZW50X21lc3NhZ2VfaWQSJGM4YTc5NDE1LWI4OGMtNGQ4OS1hODExLWExMDhiMTU2NGFkOSodCgdzOnN0aW1lEhIxNzUwOTYwODQ4NzUzLjI0NDEwBzp5MWxhV3h3SU93UkpPd1Zta2NHZXZXcGNNTEhNNXNKU1ZKRUwxbGRnNjB2aWhybERIWGx0clp0TnBIUFJZZHQ3MHp0NHl4Q1llVHNaUmVoMEtYU1dtV1BrYXZLRERWNTZlMlFpNXFjVnRHVXMxU0tJQnU2TTYzS01ZU0IkYzhhNzk0MTUtYjg4Yy00ZDg5LWE4MTEtYTEwOGIxNTY0YWQ5ShM3NTE3ODMxOTE5ODQ3MjEyNTc5UgJtcFoFbXBfcGN6DQoIaXMtcmV0cnkSATCQAQOqAQ5sZWFkc19wbGF0Zm9ybbIBB3dlYl9zZGu6AYUBdHMuMS4yYmQwMmNiOTQ4YzliNjM5ZDY5ODM1ZDQ3ZGY1ODU2MWRjNTUxMTAwMzViY2Q3ZDc1MjQ5ODA1MjJjYzcxMDE4YzRmYmU4N2QyMzE5Y2YwNTMxODYyNGNlZGExNDkxMWNhNDA2ZGVkYmViZWRkYjJlMzBmY2U4ZDRmYTAyNTc1ZMIBXHB1Yi5CUDNSckRuemd5TEdEKzZDOVB4NjhOdkZwTldKVXN6SkM0emg1dDlLbDhjTzJIMHAva2RMcnFXaXA4ZWN2eUpvUXJ5Nktmbk1lYVBZMnBtUnBtTVdaeFk9ygFgTUVVQ0lRQ2dLQnduSFJtWkgxSmxRRzdPVExKeVE2alVacCtDN1Q4cEw5QkhSV0ZmVGdJZ0Z4MWwyMjFoYjdjUC9CS2dGWE5HaWxJaXRoZzc4dkxFSVdkWDhzbWVzaVU9ugYBMcAGz5X2wgY=
function writeVarint($value) {$result = '';while (true) {if (($value & ~0x7F) === 0) {$result .= chr($value);break;} else {$result .= chr(($value & 0x7F) | 0x80);$value >>= 7;}}return $result;
}function writeVarintField($fieldNum, $value) {return writeVarint(($fieldNum << 3) | 0) . writeVarint($value);
}function writeLengthDelimitedField($fieldNum, $value) {return writeVarint(($fieldNum << 3) | 2) . writeVarint(strlen($value)) . $value;
}
function writeFixedField($fieldNum, $hex) {return writeVarint(($fieldNum << 3) | 5) . hex2bin($hex);
}
function writeFixed32Field($fieldNum, $hex4bytes) {if (strlen($hex4bytes) !== 8) throw new Exception("Fixed32 must be 4 bytes (8 hex chars)");return writeVarint(($fieldNum << 3) | 5) . hex2bin($hex4bytes);
}function writeFixed64Field($fieldNum, $hex8bytes) {if (strlen($hex8bytes) !== 16) throw new Exception("Fixed64 must be 8 bytes (16 hex chars)");return writeVarint(($fieldNum << 3) | 1) . hex2bin($hex8bytes);
}function buildField4() {return "eyJNYXJrSWQiOjAsIlR5cGUiOjEsIkFwcElkIjozODQxNTgsIlVzZXJJZCI6NzUwOTY4NjY2NTI4OTU2NTI0MywiVGltZXN0YW1wIjoxNzUwOTU1NjU0NzI1fQ==";
}function buildKeyValueMessage($key, $value) {return writeLengthDelimitedField(1, $key) . writeLengthDelimitedField(2, $value);
}function build8_100_5() {$out = '';$pairs = [['a:clue_b2c_time_log', '{"b2c_fe_send_time":1750960847462}'],['a:im_general_send_msg_time', '1750960847462'],['a:send_time_source', 'commerce_pri'],['cs:msg_channel', '1'],['cs:conv_mode', '1'],['cs:account_type', '1'],['cs:tool_id', '0'],['leads_im_chat_scene', 'commerce'],['leads_im_msg_schema', 'commerce'],[buildNestedKey(), 'Cs_LeadsPlatform'],['leads_im_biz_chat_id', ''],['leads_im_msg_platform', 'leads_platform'],['leads_pc_direct_msg', '1'],['s:mentioned_users', ''],['s:client_message_id', 'c8a79415-b88c-4d89-a811-a108b1564ad9'],['s:stime', '1750960848753.2441']];foreach ($pairs as [$k, $v]) {$entry = buildKeyValueMessage($k, $v);$out .= writeLengthDelimitedField(5, $entry);}return $out;
}function buildNestedKey() {$inner = '';$inner .= writeFixed64Field(12, '3A73616979616E5F');$inner .= writeFixed64Field(13, '6D5F73656E645F6D');$inner .= writeFixed32Field(12, '74686F64'); // ✅ 修正这里为 Fixed32,直接写入return $inner;return writeLengthDelimitedField(1, $inner);
}function build8_100() {$out = '';$out .= writeLengthDelimitedField(1, '0:1:1893773747171799:7509686665289565243');$out .= writeVarintField(2, 1);$out .= writeVarintField(3, 7517559368519336459);$out .= writeLengthDelimitedField(4, '{"text":"姐姐"}');$out .= build8_100_5();$out .= writeVarintField(6, 7);$out .= writeLengthDelimitedField(7, '1laWxwIOwRJOwVmkcGevWpcMLHM5sJSVJEL1ldg60vihrlDHXltrZtNpHPRYdt70zt4yxCYeTsZReh0KXSWmWPkavKDDV56e2Qi5qcVtGUs1SKIBu6M63KMYS');$out .= writeLengthDelimitedField(8, 'c8a79415-b88c-4d89-a811-a108b1564ad9');return $out;
}function buildFinalMessage() {$out = '';$out .= writeVarintField(1, 100);$out .= writeVarintField(2, 11543);$out .= writeLengthDelimitedField(3, "1.2.1");$out .= writeLengthDelimitedField(4, buildField4());$out .= writeVarintField(5, 3);$out .= writeVarintField(6, 0);$out .= writeLengthDelimitedField(7, "c89e8df:master");$out .= writeLengthDelimitedField(8, writeLengthDelimitedField(100, build8_100()));$out .= writeLengthDelimitedField(9, "7517831919847212579");$out .= writeLengthDelimitedField(10, "mp");$out .= writeLengthDelimitedField(11, chr((13 << 3) | 5) . hex2bin("705F7063"));$out .= writeLengthDelimitedField(15, writeLengthDelimitedField(1, "is-retry") . writeLengthDelimitedField(2, "0"));$out .= writeVarintField(18, 3);$out .= writeLengthDelimitedField(21, "leads_platform");$out .= writeLengthDelimitedField(22, "web_sdk");$out .= writeLengthDelimitedField(23, hex2bin("74732E312E3262643032636239343863396236333964363938333564343764663538353631646335353131303033356263643764373532343938303532326363373130313863346662653837643233313963663035333138363234636564613134393131636134303664656462656265646462326533306663653864346661303235373564"));$out .= writeLengthDelimitedField(24, hex2bin("7075622E4250335272446E7A67794C47442B364339507836384E7646704E574A55737A4A43347A683574394B6C38634F324830702F6B644C727157697038656376794A6F517279364B666E4D6561505932706D52706D4D575A78593D"));$out .= writeLengthDelimitedField(25, hex2bin("4D455543495143674B42776E48526D5A48314A6C5147374F544C4A7951366A555A702B43375438704C39424852574666546749674678316C32323168623763502F424B6746584E47696C49697468673738764C454957645838736D657369553D"));$out .= writeLengthDelimitedField(103, "1");$out .= writeVarintField(104, 1750960847);return $out;
}$protobuf = buildFinalMessage();
echo base64_encode($protobuf);