Skip to content

Commit 89aaf71

Browse files
authored
Serialize protobuf enums as integer (#1192)
Spec: "Values of enum fields MUST be encoded as integer values."
1 parent 756092b commit 89aaf71

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

ProtobufSerializer.php

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,23 @@
88
use function base64_decode;
99
use function bin2hex;
1010
use Exception;
11+
use function get_class;
12+
use Google\Protobuf\Descriptor;
13+
use Google\Protobuf\DescriptorPool;
14+
use Google\Protobuf\FieldDescriptor;
15+
use Google\Protobuf\Internal\GPBLabel;
16+
use Google\Protobuf\Internal\GPBType;
1117
use Google\Protobuf\Internal\Message;
1218
use InvalidArgumentException;
19+
use function json_decode;
20+
use function json_encode;
21+
use const JSON_UNESCAPED_SLASHES;
22+
use const JSON_UNESCAPED_UNICODE;
23+
use function lcfirst;
1324
use OpenTelemetry\SDK\Common\Export\TransportInterface;
25+
use function property_exists;
1426
use function sprintf;
27+
use function ucwords;
1528

1629
/**
1730
* @internal
@@ -83,10 +96,9 @@ public function serialize(Message $message): string
8396
case self::PROTOBUF:
8497
return $message->serializeToString();
8598
case self::JSON:
86-
//@todo https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md#json-protobuf-encoding
87-
return $message->serializeToJsonString();
99+
return self::postProcessJsonEnumValues($message, $message->serializeToJsonString());
88100
case self::NDJSON:
89-
return $message->serializeToJsonString() . "\n";
101+
return self::postProcessJsonEnumValues($message, $message->serializeToJsonString()) . "\n";
90102
default:
91103
throw new AssertionError();
92104
}
@@ -112,4 +124,69 @@ public function hydrate(Message $message, string $payload): void
112124
throw new AssertionError();
113125
}
114126
}
127+
128+
/**
129+
* Workaround until protobuf exposes `FormatEnumsAsIntegers` option.
130+
*
131+
* [JSON Protobuf Encoding](https://opentelemetry.io/docs/specs/otlp/#json-protobuf-encoding):
132+
* > Values of enum fields MUST be encoded as integer values.
133+
*
134+
* @see https://github.com/open-telemetry/opentelemetry-php/issues/978
135+
* @see https://github.com/protocolbuffers/protobuf/pull/12707
136+
*/
137+
private static function postProcessJsonEnumValues(Message $message, string $payload): string
138+
{
139+
$pool = DescriptorPool::getGeneratedPool();
140+
$desc = $pool->getDescriptorByClassName(get_class($message));
141+
if (!$desc instanceof Descriptor) {
142+
return $payload;
143+
}
144+
145+
$data = json_decode($payload);
146+
unset($payload);
147+
self::traverseDescriptor($data, $desc);
148+
149+
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
150+
}
151+
152+
private static function traverseDescriptor(object $data, Descriptor $desc): void
153+
{
154+
for ($i = 0, $n = $desc->getFieldCount(); $i < $n; $i++) {
155+
// @phan-suppress-next-line PhanParamTooManyInternal
156+
$field = $desc->getField($i);
157+
$name = lcfirst(strtr(ucwords($field->getName(), '_'), ['_' => '']));
158+
if (!property_exists($data, $name)) {
159+
continue;
160+
}
161+
162+
if ($field->getLabel() === GPBLabel::REPEATED) {
163+
foreach ($data->$name as $key => $value) {
164+
$data->$name[$key] = self::traverseFieldDescriptor($value, $field);
165+
}
166+
} else {
167+
$data->$name = self::traverseFieldDescriptor($data->$name, $field);
168+
}
169+
}
170+
}
171+
172+
private static function traverseFieldDescriptor($data, FieldDescriptor $field)
173+
{
174+
switch ($field->getType()) {
175+
case GPBType::MESSAGE:
176+
self::traverseDescriptor($data, $field->getMessageType());
177+
178+
break;
179+
case GPBType::ENUM:
180+
$enum = $field->getEnumType();
181+
for ($i = 0, $n = $enum->getValueCount(); $i < $n; $i++) {
182+
if ($data === $enum->getValue($i)->getName()) {
183+
return $enum->getValue($i)->getNumber();
184+
}
185+
}
186+
187+
break;
188+
}
189+
190+
return $data;
191+
}
115192
}

0 commit comments

Comments
 (0)