Skip to content

Commit 40ec76e

Browse files
brettmccopybara-github
authored andcommitted
[PHP] allow encoding enums as integers for json (#12707)
The php extension already had an implementation for encoding enums as integers, but it was not exposed, so add a bitmask param to `Message::serializeToJsonString()` to enable it as well as `preserve_proto_field_names`. Converted the existing boolean parameter to preferentially be an integer (with boolean handling for BC), which accepts a bitmask of `Google\Protobuf\PrintOptions::*`, eg `PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS | PrintOptions::PRESERVE_PROTO_FIELD_NAMES` `PrintOptions` class name and constant names were chosen to align with protobuf-cpp's implementation. Implemented `preserve_proto_fieldnames` in the native version, to match the existing implementation in the extension (with tests). Closes #12707 COPYBARA_INTEGRATE_REVIEW=#12707 from brettmc:php-json-enum-as-int 515a083 PiperOrigin-RevId: 725806573
1 parent 0717c15 commit 40ec76e

File tree

12 files changed

+227
-33
lines changed

12 files changed

+227
-33
lines changed

php/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ _RUNTIME_SOURCES = [
4747
"src/Google/Protobuf/Internal/MessageBuilderContext.php",
4848
"src/Google/Protobuf/Internal/OneofDescriptor.php",
4949
"src/Google/Protobuf/Internal/OneofField.php",
50+
"src/Google/Protobuf/PrintOptions.php",
5051
"src/Google/Protobuf/Internal/RawInputStream.php",
5152
"src/Google/Protobuf/Internal/RepeatedField.php",
5253
"src/Google/Protobuf/Internal/RepeatedFieldIter.php",

php/ext/google/protobuf/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ if test "$PHP_PROTOBUF" != "no"; then
44

55
PHP_NEW_EXTENSION(
66
protobuf,
7-
arena.c array.c convert.c def.c map.c message.c names.c php-upb.c protobuf.c third_party/utf8_range/utf8_range.c,
7+
arena.c array.c convert.c def.c map.c message.c names.c print_options.c php-upb.c protobuf.c third_party/utf8_range/utf8_range.c,
88
$ext_shared, , -std=gnu99 -I@ext_srcdir@/third_party/utf8_range)
99
PHP_ADD_BUILD_DIR($ext_builddir/third_party/utf8_range)
1010

php/ext/google/protobuf/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ ARG_ENABLE("protobuf", "whether to enable Protobuf extension", "no");
33
if (PHP_PROTOBUF != "no") {
44
ADD_SOURCES(configure_module_dirname + "/third_party/utf8_range", "utf8_range.c", "PROTOBUF");
55
ADD_FLAG("CFLAGS_PROTOBUF", "/I" + configure_module_dirname + "/third_party/utf8_range");
6-
EXTENSION("protobuf", "arena.c array.c convert.c def.c map.c message.c names.c php-upb.c protobuf.c");
6+
EXTENSION("protobuf", "arena.c array.c convert.c def.c map.c message.c names.c print_options.c php-upb.c protobuf.c");
77
}

php/ext/google/protobuf/message.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "def.h"
2222
#include "map.h"
2323
#include "php-upb.h"
24+
#include "print_options.h"
2425
#include "protobuf.h"
2526

2627
// -----------------------------------------------------------------------------
@@ -754,16 +755,24 @@ PHP_METHOD(Message, serializeToJsonString) {
754755
size_t size;
755756
int options = 0;
756757
char buf[1024];
757-
zend_bool preserve_proto_fieldnames = false;
758+
zval* flags = NULL;
758759
upb_Status status;
759760

760-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b",
761-
&preserve_proto_fieldnames) == FAILURE) {
761+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &flags) == FAILURE) {
762762
return;
763763
}
764764

765-
if (preserve_proto_fieldnames) {
765+
if (flags == NULL || Z_TYPE_P(flags) == IS_FALSE) {
766+
// do nothing
767+
} else if (Z_TYPE_P(flags) == IS_TRUE) {
766768
options |= upb_JsonEncode_UseProtoNames;
769+
} else if (Z_TYPE_P(flags) == IS_LONG) {
770+
if (Z_LVAL_P(flags) & ALWAYS_PRINT_ENUMS_AS_INTS) {
771+
options |= upb_JsonEncode_FormatEnumsAsIntegers;
772+
}
773+
if (Z_LVAL_P(flags) & PRESERVE_PROTO_FIELD_NAMES) {
774+
options |= upb_JsonEncode_UseProtoNames;
775+
}
767776
}
768777

769778
upb_Status_Clear(&status);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Protocol Buffers - Google's data interchange format
2+
// Copyright 2008 Google Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file or at
6+
// https://developers.google.com/open-source/licenses/bsd
7+
8+
#include "print_options.h"
9+
10+
#include "php.h"
11+
12+
zend_class_entry* options_ce;
13+
14+
void PrintOptions_ModuleInit() {
15+
zend_class_entry tmp_ce;
16+
17+
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\PrintOptions", NULL);
18+
options_ce = zend_register_internal_class(&tmp_ce);
19+
20+
// Define constants
21+
zend_declare_class_constant_long(options_ce, "PRESERVE_PROTO_FIELD_NAMES",
22+
sizeof("PRESERVE_PROTO_FIELD_NAMES") - 1,
23+
PRESERVE_PROTO_FIELD_NAMES);
24+
zend_declare_class_constant_long(options_ce, "ALWAYS_PRINT_ENUMS_AS_INTS",
25+
sizeof("ALWAYS_PRINT_ENUMS_AS_INTS") - 1,
26+
ALWAYS_PRINT_ENUMS_AS_INTS);
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Protocol Buffers - Google's data interchange format
2+
// Copyright 2008 Google Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file or at
6+
// https://developers.google.com/open-source/licenses/bsd
7+
8+
#ifndef PHP_PROTOBUF_PRINT_OPTIONS_H_
9+
#define PHP_PROTOBUF_PRINT_OPTIONS_H_
10+
11+
#define PRESERVE_PROTO_FIELD_NAMES (1 << 0)
12+
#define ALWAYS_PRINT_ENUMS_AS_INTS (1 << 1)
13+
14+
// Registers the PHP PrintOptions class.
15+
void PrintOptions_ModuleInit();
16+
17+
#endif // PHP_PROTOBUF_PRINT_OPTIONS_H_

php/ext/google/protobuf/protobuf.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "map.h"
1818
#include "message.h"
1919
#include "names.h"
20+
#include "print_options.h"
2021

2122
// -----------------------------------------------------------------------------
2223
// Module "globals"
@@ -292,6 +293,7 @@ static PHP_MINIT_FUNCTION(protobuf) {
292293
Def_ModuleInit();
293294
Map_ModuleInit();
294295
Message_ModuleInit();
296+
PrintOptions_ModuleInit();
295297
return SUCCESS;
296298
}
297299

php/src/Google/Protobuf/Internal/CodedOutputStream.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,28 @@ class CodedOutputStream
1515
private $buffer;
1616
private $buffer_size;
1717
private $current;
18+
private $options;
1819

1920
const MAX_VARINT64_BYTES = 10;
2021

21-
public function __construct($size)
22+
public function __construct($size, $options = 0)
2223
{
2324
$this->current = 0;
2425
$this->buffer_size = $size;
2526
$this->buffer = str_repeat(chr(0), $this->buffer_size);
27+
$this->options = $options;
2628
}
2729

2830
public function getData()
2931
{
3032
return $this->buffer;
3133
}
3234

35+
public function getOptions()
36+
{
37+
return $this->options;
38+
}
39+
3340
public function writeVarint32($value, $trim)
3441
{
3542
$bytes = str_repeat(chr(0), self::MAX_VARINT64_BYTES);

php/src/Google/Protobuf/Internal/GPBJsonWire.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace Google\Protobuf\Internal;
1111

12+
use Google\Protobuf\PrintOptions;
13+
1214
class GPBJsonWire
1315
{
1416

@@ -19,7 +21,7 @@ public static function serializeFieldToStream(
1921
{
2022
if ($has_field_name) {
2123
$output->writeRaw("\"", 1);
22-
$field_name = GPBJsonWire::formatFieldName($field);
24+
$field_name = GPBJsonWire::formatFieldName($field, $output->getOptions());
2325
$output->writeRaw($field_name, strlen($field_name));
2426
$output->writeRaw("\":", 2);
2527
}
@@ -178,6 +180,11 @@ private static function serializeSingularFieldValueToStream(
178180
$output->writeRaw("null", 4);
179181
break;
180182
}
183+
if ($output->getOptions() & PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS) {
184+
$str_value = strval($value);
185+
$output->writeRaw($str_value, strlen($str_value));
186+
break;
187+
}
181188
$enum_value_desc = $enum_desc->getValueByNumber($value);
182189
if (!is_null($enum_value_desc)) {
183190
$str_value = $enum_value_desc->getName();
@@ -220,8 +227,11 @@ private static function serializeSingularFieldValueToStream(
220227
return true;
221228
}
222229

223-
private static function formatFieldName($field)
230+
private static function formatFieldName($field, $options)
224231
{
232+
if ($options & PrintOptions::PRESERVE_PROTO_FIELD_NAMES) {
233+
return $field->getName();
234+
}
225235
return $field->getJsonName();
226236
}
227237

php/src/Google/Protobuf/Internal/Message.php

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Google\Protobuf\Internal\MapEntry;
2323
use Google\Protobuf\Internal\RepeatedField;
2424
use Google\Protobuf\ListValue;
25+
use Google\Protobuf\PrintOptions;
2526
use Google\Protobuf\Value;
2627
use Google\Protobuf\Struct;
2728
use Google\Protobuf\NullValue;
@@ -1570,9 +1571,9 @@ public function serializeToString()
15701571
* Serialize the message to json string.
15711572
* @return string Serialized json protobuf data.
15721573
*/
1573-
public function serializeToJsonString()
1574+
public function serializeToJsonString($options = 0)
15741575
{
1575-
$output = new CodedOutputStream($this->jsonByteSize());
1576+
$output = new CodedOutputStream($this->jsonByteSize($options), $options);
15761577
$this->serializeToJsonStream($output);
15771578
return $output->getData();
15781579
}
@@ -1685,7 +1686,7 @@ private function fieldDataOnlyByteSize($field, $value)
16851686
/**
16861687
* @ignore
16871688
*/
1688-
private function fieldDataOnlyJsonByteSize($field, $value)
1689+
private function fieldDataOnlyJsonByteSize($field, $value, $options = 0)
16891690
{
16901691
$size = 0;
16911692

@@ -1742,13 +1743,17 @@ private function fieldDataOnlyJsonByteSize($field, $value)
17421743
$size += 4;
17431744
break;
17441745
}
1745-
$enum_value_desc = $enum_desc->getValueByNumber($value);
1746-
if (!is_null($enum_value_desc)) {
1747-
$size += 2; // size for ""
1748-
$size += strlen($enum_value_desc->getName());
1746+
if ($options & PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS) {
1747+
$size += strlen(strval($value)); // size for integer length
17491748
} else {
1750-
$str_value = strval($value);
1751-
$size += strlen($str_value);
1749+
$enum_value_desc = $enum_desc->getValueByNumber($value);
1750+
if (!is_null($enum_value_desc)) {
1751+
$size += 2; // size for ""
1752+
$size += strlen($enum_value_desc->getName());
1753+
} else {
1754+
$str_value = strval($value);
1755+
$size += strlen($str_value);
1756+
}
17521757
}
17531758
break;
17541759
case GPBType::BOOL:
@@ -1773,7 +1778,7 @@ private function fieldDataOnlyJsonByteSize($field, $value)
17731778
$size += 2; // size for \"\"
17741779
break;
17751780
case GPBType::MESSAGE:
1776-
$size += $value->jsonByteSize();
1781+
$size += $value->jsonByteSize($options);
17771782
break;
17781783
# case GPBType::GROUP:
17791784
# // TODO: Add support.
@@ -1851,7 +1856,7 @@ private function fieldByteSize($field)
18511856
/**
18521857
* @ignore
18531858
*/
1854-
private function fieldJsonByteSize($field)
1859+
private function fieldJsonByteSize($field, $options = 0)
18551860
{
18561861
$size = 0;
18571862

@@ -1862,7 +1867,11 @@ private function fieldJsonByteSize($field)
18621867
if ($count !== 0) {
18631868
if (!GPBUtil::hasSpecialJsonMapping($this)) {
18641869
$size += 3; // size for "\"\":".
1865-
$size += strlen($field->getJsonName()); // size for field name
1870+
if ($options & PrintOptions::PRESERVE_PROTO_FIELD_NAMES) {
1871+
$size += strlen($field->getName());
1872+
} else {
1873+
$size += strlen($field->getJsonName());
1874+
} // size for field name
18661875
}
18671876
$size += 2; // size for "{}".
18681877
$size += $count - 1; // size for commas
@@ -1886,8 +1895,8 @@ private function fieldJsonByteSize($field)
18861895
if ($additional_quote) {
18871896
$size += 2; // size for ""
18881897
}
1889-
$size += $this->fieldDataOnlyJsonByteSize($key_field, $key);
1890-
$size += $this->fieldDataOnlyJsonByteSize($value_field, $value);
1898+
$size += $this->fieldDataOnlyJsonByteSize($key_field, $key, $options);
1899+
$size += $this->fieldDataOnlyJsonByteSize($value_field, $value, $options);
18911900
$size += 1; // size for :
18921901
}
18931902
}
@@ -1898,23 +1907,31 @@ private function fieldJsonByteSize($field)
18981907
if ($count !== 0) {
18991908
if (!GPBUtil::hasSpecialJsonMapping($this)) {
19001909
$size += 3; // size for "\"\":".
1901-
$size += strlen($field->getJsonName()); // size for field name
1910+
if ($options & PrintOptions::PRESERVE_PROTO_FIELD_NAMES) {
1911+
$size += strlen($field->getName());
1912+
} else {
1913+
$size += strlen($field->getJsonName());
1914+
} // size for field name
19021915
}
19031916
$size += 2; // size for "[]".
19041917
$size += $count - 1; // size for commas
19051918
$getter = $field->getGetter();
19061919
foreach ($values as $value) {
1907-
$size += $this->fieldDataOnlyJsonByteSize($field, $value);
1920+
$size += $this->fieldDataOnlyJsonByteSize($field, $value, $options);
19081921
}
19091922
}
19101923
} elseif ($this->existField($field) || GPBUtil::hasJsonValue($this)) {
19111924
if (!GPBUtil::hasSpecialJsonMapping($this)) {
19121925
$size += 3; // size for "\"\":".
1913-
$size += strlen($field->getJsonName()); // size for field name
1926+
if ($options & PrintOptions::PRESERVE_PROTO_FIELD_NAMES) {
1927+
$size += strlen($field->getName());
1928+
} else {
1929+
$size += strlen($field->getJsonName());
1930+
} // size for field name
19141931
}
19151932
$getter = $field->getGetter();
19161933
$value = $this->$getter();
1917-
$size += $this->fieldDataOnlyJsonByteSize($field, $value);
1934+
$size += $this->fieldDataOnlyJsonByteSize($field, $value, $options);
19181935
}
19191936
return $size;
19201937
}
@@ -1963,7 +1980,7 @@ private function kvUpdateHelper($field, $update_key, $update_value)
19631980
/**
19641981
* @ignore
19651982
*/
1966-
public function jsonByteSize()
1983+
public function jsonByteSize($options = 0)
19671984
{
19681985
$size = 0;
19691986
if (is_a($this, 'Google\Protobuf\Any')) {
@@ -1980,9 +1997,9 @@ public function jsonByteSize()
19801997
if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
19811998
// Size for "\",value\":".
19821999
$size += 9;
1983-
$size += $value_msg->jsonByteSize();
2000+
$size += $value_msg->jsonByteSize($options);
19842001
} else {
1985-
$value_size = $value_msg->jsonByteSize();
2002+
$value_size = $value_msg->jsonByteSize($options);
19862003
// size === 2 it's empty message {} which is not serialized inside any
19872004
if ($value_size !== 2) {
19882005
// Size for value. +1 for comma, -2 for "{}".
@@ -2002,7 +2019,7 @@ public function jsonByteSize()
20022019
} elseif (get_class($this) === 'Google\Protobuf\ListValue') {
20032020
$field = $this->desc->getField()[1];
20042021
if ($this->existField($field)) {
2005-
$field_size = $this->fieldJsonByteSize($field);
2022+
$field_size = $this->fieldJsonByteSize($field, $options);
20062023
$size += $field_size;
20072024
} else {
20082025
// Size for "[]".
@@ -2011,7 +2028,7 @@ public function jsonByteSize()
20112028
} elseif (get_class($this) === 'Google\Protobuf\Struct') {
20122029
$field = $this->desc->getField()[1];
20132030
if ($this->existField($field)) {
2014-
$field_size = $this->fieldJsonByteSize($field);
2031+
$field_size = $this->fieldJsonByteSize($field, $options);
20152032
$size += $field_size;
20162033
} else {
20172034
// Size for "{}".
@@ -2026,7 +2043,7 @@ public function jsonByteSize()
20262043
$fields = $this->desc->getField();
20272044
$count = 0;
20282045
foreach ($fields as $field) {
2029-
$field_size = $this->fieldJsonByteSize($field);
2046+
$field_size = $this->fieldJsonByteSize($field, $options);
20302047
$size += $field_size;
20312048
if ($field_size != 0) {
20322049
$count++;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
// Protocol Buffers - Google's data interchange format
4+
// Copyright 2008 Google Inc. All rights reserved.
5+
//
6+
// Use of this source code is governed by a BSD-style
7+
// license that can be found in the LICENSE file or at
8+
// https://developers.google.com/open-source/licenses/bsd
9+
10+
namespace Google\Protobuf;
11+
12+
class PrintOptions
13+
{
14+
const PRESERVE_PROTO_FIELD_NAMES = 1 << 0;
15+
const ALWAYS_PRINT_ENUMS_AS_INTS = 1 << 1;
16+
}

0 commit comments

Comments
 (0)