aboutsummaryrefslogtreecommitdiff
path: root/php
diff options
context:
space:
mode:
authorPaul Yang <TeBoring@users.noreply.github.com>2017-11-03 12:30:09 -0700
committerGitHub <noreply@github.com>2017-11-03 12:30:09 -0700
commitbcda919ceeae8bd854c07a8300d8996ef52315db (patch)
treef6508e99d455a4aeed37714399026d3e44dc96e8 /php
parent239dba535c48b64769cdfdfcb18f771620ccbba9 (diff)
downloadprotobuf-bcda919ceeae8bd854c07a8300d8996ef52315db.tar.gz
protobuf-bcda919ceeae8bd854c07a8300d8996ef52315db.tar.bz2
protobuf-bcda919ceeae8bd854c07a8300d8996ef52315db.zip
Fix php well known type conformance tests (#3828)
* Fix php well known type conformance tests * Properly generate code for test.proto * Provide GPBMetadata files in c extensions for generated files to import. * Remove unnecessary test * Clean up code * Add declaration for initOnce. * Refactoring
Diffstat (limited to 'php')
-rw-r--r--php/ext/google/protobuf/message.c42
-rw-r--r--php/ext/google/protobuf/protobuf.c11
-rw-r--r--php/ext/google/protobuf/protobuf.h24
-rw-r--r--php/src/Google/Protobuf/Any.php8
-rw-r--r--php/src/Google/Protobuf/Internal/GPBJsonWire.php47
-rw-r--r--php/src/Google/Protobuf/Internal/GPBUtil.php139
-rw-r--r--php/src/Google/Protobuf/Internal/Message.php286
7 files changed, 508 insertions, 49 deletions
diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c
index 291f5c24..3fce2c17 100644
--- a/php/ext/google/protobuf/message.c
+++ b/php/ext/google/protobuf/message.c
@@ -2029,3 +2029,45 @@ PHP_PROTO_ONEOF_FIELD_ACCESSORS(Value, value, BoolValue, "bool_value")
PHP_PROTO_ONEOF_FIELD_ACCESSORS(Value, value, StructValue, "struct_value")
PHP_PROTO_ONEOF_FIELD_ACCESSORS(Value, value, ListValue, "list_value")
PHP_PROTO_ONEOF_ACCESSORS(Value, value, Kind, "kind")
+
+// -----------------------------------------------------------------------------
+// GPBMetadata files for well known types
+// -----------------------------------------------------------------------------
+
+#define DEFINE_GPBMETADATA_FILE(LOWERNAME, CAMELNAME, CLASSNAME) \
+ zend_class_entry* gpb_metadata_##LOWERNAME##_type; \
+ static zend_function_entry gpb_metadata_##LOWERNAME##_methods[] = { \
+ PHP_ME(GPBMetadata_##CAMELNAME, initOnce, NULL, \
+ ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) \
+ ZEND_FE_END \
+ }; \
+ void gpb_metadata_##LOWERNAME##_init(TSRMLS_D) { \
+ zend_class_entry class_type; \
+ INIT_CLASS_ENTRY(class_type, CLASSNAME, \
+ gpb_metadata_##LOWERNAME##_methods); \
+ gpb_metadata_##LOWERNAME##_type = \
+ zend_register_internal_class(&class_type TSRMLS_CC); \
+ } \
+ PHP_METHOD(GPBMetadata_##CAMELNAME, initOnce) { \
+ init_file_##LOWERNAME(TSRMLS_C); \
+ }
+
+DEFINE_GPBMETADATA_FILE(any, Any, "GPBMetadata\\Google\\Protobuf\\Any");
+DEFINE_GPBMETADATA_FILE(api, Api, "GPBMetadata\\Google\\Protobuf\\Api");
+DEFINE_GPBMETADATA_FILE(duration, Duration,
+ "GPBMetadata\\Google\\Protobuf\\Duration");
+DEFINE_GPBMETADATA_FILE(field_mask, FieldMask,
+ "GPBMetadata\\Google\\Protobuf\\FieldMask");
+DEFINE_GPBMETADATA_FILE(empty, Empty,
+ "GPBMetadata\\Google\\Protobuf\\GPBEmpty");
+DEFINE_GPBMETADATA_FILE(source_context, SourceContext,
+ "GPBMetadata\\Google\\Protobuf\\SourceContext");
+DEFINE_GPBMETADATA_FILE(struct, Struct,
+ "GPBMetadata\\Google\\Protobuf\\Struct");
+DEFINE_GPBMETADATA_FILE(timestamp, Timestamp,
+ "GPBMetadata\\Google\\Protobuf\\Timestamp");
+DEFINE_GPBMETADATA_FILE(type, Type, "GPBMetadata\\Google\\Protobuf\\Type");
+DEFINE_GPBMETADATA_FILE(wrappers, Wrappers,
+ "GPBMetadata\\Google\\Protobuf\\Wrappers");
+
+#undef DEFINE_GPBMETADATA_FILE
diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c
index b67089e0..265d636e 100644
--- a/php/ext/google/protobuf/protobuf.c
+++ b/php/ext/google/protobuf/protobuf.c
@@ -300,6 +300,17 @@ static PHP_MINIT_FUNCTION(protobuf) {
repeated_field_iter_init(TSRMLS_C);
util_init(TSRMLS_C);
+ gpb_metadata_any_init(TSRMLS_C);
+ gpb_metadata_api_init(TSRMLS_C);
+ gpb_metadata_duration_init(TSRMLS_C);
+ gpb_metadata_field_mask_init(TSRMLS_C);
+ gpb_metadata_empty_init(TSRMLS_C);
+ gpb_metadata_source_context_init(TSRMLS_C);
+ gpb_metadata_struct_init(TSRMLS_C);
+ gpb_metadata_timestamp_init(TSRMLS_C);
+ gpb_metadata_type_init(TSRMLS_C);
+ gpb_metadata_wrappers_init(TSRMLS_C);
+
any_init(TSRMLS_C);
api_init(TSRMLS_C);
bool_value_init(TSRMLS_C);
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index cb098747..18343772 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -610,7 +610,6 @@ typedef struct BytesValue BytesValue;
typedef struct Descriptor Descriptor;
typedef struct Descriptor Descriptor;
typedef struct DescriptorPool DescriptorPool;
-typedef struct DescriptorPool DescriptorPool;
typedef struct DoubleValue DoubleValue;
typedef struct Duration Duration;
typedef struct Enum Enum;
@@ -630,7 +629,6 @@ typedef struct GPBEmpty GPBEmpty;
typedef struct Int32Value Int32Value;
typedef struct Int64Value Int64Value;
typedef struct InternalDescriptorPool InternalDescriptorPool;
-typedef struct InternalDescriptorPool InternalDescriptorPool;
typedef struct ListValue ListValue;
typedef struct Map Map;
typedef struct Map Map;
@@ -714,6 +712,17 @@ void uint64_value_init(TSRMLS_D);
void util_init(TSRMLS_D);
void value_init(TSRMLS_D);
+void gpb_metadata_any_init(TSRMLS_D);
+void gpb_metadata_api_init(TSRMLS_D);
+void gpb_metadata_duration_init(TSRMLS_D);
+void gpb_metadata_field_mask_init(TSRMLS_D);
+void gpb_metadata_empty_init(TSRMLS_D);
+void gpb_metadata_source_context_init(TSRMLS_D);
+void gpb_metadata_struct_init(TSRMLS_D);
+void gpb_metadata_timestamp_init(TSRMLS_D);
+void gpb_metadata_type_init(TSRMLS_D);
+void gpb_metadata_wrappers_init(TSRMLS_D);
+
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
// instances.
void add_def_obj(const void* def, PHP_PROTO_HASHTABLE_VALUE value);
@@ -1170,6 +1179,17 @@ extern zend_class_entry* oneof_descriptor_type;
// Well Known Type.
// -----------------------------------------------------------------------------
+PHP_METHOD(GPBMetadata_Any, initOnce);
+PHP_METHOD(GPBMetadata_Api, initOnce);
+PHP_METHOD(GPBMetadata_Duration, initOnce);
+PHP_METHOD(GPBMetadata_FieldMask, initOnce);
+PHP_METHOD(GPBMetadata_Empty, initOnce);
+PHP_METHOD(GPBMetadata_SourceContext, initOnce);
+PHP_METHOD(GPBMetadata_Struct, initOnce);
+PHP_METHOD(GPBMetadata_Timestamp, initOnce);
+PHP_METHOD(GPBMetadata_Type, initOnce);
+PHP_METHOD(GPBMetadata_Wrappers, initOnce);
+
PHP_METHOD(Any, __construct);
PHP_METHOD(Any, getTypeUrl);
PHP_METHOD(Any, setTypeUrl);
diff --git a/php/src/Google/Protobuf/Any.php b/php/src/Google/Protobuf/Any.php
index a39c4e6a..91ba4bd5 100644
--- a/php/src/Google/Protobuf/Any.php
+++ b/php/src/Google/Protobuf/Any.php
@@ -207,9 +207,9 @@ class Any extends \Google\Protobuf\Internal\Message
public function unpack()
{
// Get fully qualifed name from type url.
- $url_prifix_len = strlen(Any::TYPE_URL_PREFIX);
+ $url_prifix_len = strlen(GPBUtil::TYPE_URL_PREFIX);
if (substr($this->type_url, 0, $url_prifix_len) !=
- Any::TYPE_URL_PREFIX) {
+ GPBUtil::TYPE_URL_PREFIX) {
throw new \Exception(
"Type url needs to be type.googleapis.com/fully-qulified");
}
@@ -251,7 +251,7 @@ class Any extends \Google\Protobuf\Internal\Message
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class($msg));
$fully_qualifed_name = $desc->getFullName();
- $this->type_url = Any::TYPE_URL_PREFIX.substr(
+ $this->type_url = GPBUtil::TYPE_URL_PREFIX.substr(
$fully_qualifed_name, 1, strlen($fully_qualifed_name));
}
@@ -265,7 +265,7 @@ class Any extends \Google\Protobuf\Internal\Message
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName($klass);
$fully_qualifed_name = $desc->getFullName();
- $type_url = Any::TYPE_URL_PREFIX.substr(
+ $type_url = GPBUtil::TYPE_URL_PREFIX.substr(
$fully_qualifed_name, 1, strlen($fully_qualifed_name));
return $this->type_url === $type_url;
}
diff --git a/php/src/Google/Protobuf/Internal/GPBJsonWire.php b/php/src/Google/Protobuf/Internal/GPBJsonWire.php
index 97789356..9ae57ab3 100644
--- a/php/src/Google/Protobuf/Internal/GPBJsonWire.php
+++ b/php/src/Google/Protobuf/Internal/GPBJsonWire.php
@@ -38,19 +38,26 @@ class GPBJsonWire
public static function serializeFieldToStream(
$value,
$field,
- &$output)
+ &$output, $has_field_name = true)
{
- $output->writeRaw("\"", 1);
- $field_name = GPBJsonWire::formatFieldName($field);
- $output->writeRaw($field_name, strlen($field_name));
- $output->writeRaw("\":", 2);
- return static::serializeFieldValueToStream($value, $field, $output);
+ if ($has_field_name) {
+ $output->writeRaw("\"", 1);
+ $field_name = GPBJsonWire::formatFieldName($field);
+ $output->writeRaw($field_name, strlen($field_name));
+ $output->writeRaw("\":", 2);
+ }
+ return static::serializeFieldValueToStream(
+ $value,
+ $field,
+ $output,
+ !$has_field_name);
}
- private static function serializeFieldValueToStream(
+ public static function serializeFieldValueToStream(
$values,
$field,
- &$output)
+ &$output,
+ $is_well_known = false)
{
if ($field->isMap()) {
$output->writeRaw("{", 1);
@@ -84,7 +91,8 @@ class GPBJsonWire
if (!static::serializeSingularFieldValueToStream(
$key,
$key_field,
- $output)) {
+ $output,
+ $is_well_known)) {
return false;
}
if ($additional_quote) {
@@ -94,7 +102,8 @@ class GPBJsonWire
if (!static::serializeSingularFieldValueToStream(
$value,
$value_field,
- $output)) {
+ $output,
+ $is_well_known)) {
return false;
}
}
@@ -112,7 +121,8 @@ class GPBJsonWire
if (!static::serializeSingularFieldValueToStream(
$value,
$field,
- $output)) {
+ $output,
+ $is_well_known)) {
return false;
}
}
@@ -122,14 +132,15 @@ class GPBJsonWire
return static::serializeSingularFieldValueToStream(
$values,
$field,
- $output);
+ $output,
+ $is_well_known);
}
}
private static function serializeSingularFieldValueToStream(
$value,
$field,
- &$output)
+ &$output, $is_well_known = false)
{
switch ($field->getType()) {
case GPBType::SFIXED32:
@@ -186,6 +197,10 @@ class GPBJsonWire
break;
case GPBType::ENUM:
$enum_desc = $field->getEnumType();
+ if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
+ $output->writeRaw("null", 4);
+ break;
+ }
$enum_value_desc = $enum_desc->getValueByNumber($value);
if (!is_null($enum_value_desc)) {
$str_value = $enum_value_desc->getName();
@@ -205,7 +220,11 @@ class GPBJsonWire
}
break;
case GPBType::BYTES:
- $value = base64_encode($value);
+ $bytes_value = base64_encode($value);
+ $output->writeRaw("\"", 1);
+ $output->writeRaw($bytes_value, strlen($bytes_value));
+ $output->writeRaw("\"", 1);
+ break;
case GPBType::STRING:
$value = json_encode($value);
$output->writeRaw($value, strlen($value));
diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php
index 7fa4a673..76f84cbd 100644
--- a/php/src/Google/Protobuf/Internal/GPBUtil.php
+++ b/php/src/Google/Protobuf/Internal/GPBUtil.php
@@ -32,14 +32,29 @@
namespace Google\Protobuf\Internal;
+use Google\Protobuf\Duration;
+use Google\Protobuf\FieldMask;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\MapField;
+function camel2underscore($input) {
+ preg_match_all(
+ '!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!',
+ $input,
+ $matches);
+ $ret = $matches[0];
+ foreach ($ret as &$match) {
+ $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
+ }
+ return implode('_', $ret);
+}
+
class GPBUtil
{
const NANOS_PER_MILLISECOND = 1000000;
const NANOS_PER_MICROSECOND = 1000;
+ const TYPE_URL_PREFIX = 'type.googleapis.com/';
public static function divideInt64ToInt32($value, &$high, &$low, $trim = false)
{
@@ -358,7 +373,7 @@ class GPBUtil
}
return $result;
}
-
+
public static function parseTimestamp($timestamp)
{
// prevent parsing timestamps containing with the non-existant year "0000"
@@ -370,7 +385,7 @@ class GPBUtil
if (substr($timestamp, -1, 1) === "z") {
throw new \Exception("Timezone cannot be a lowercase z.");
}
-
+
$nanoseconds = 0;
$periodIndex = strpos($timestamp, ".");
if ($periodIndex !== false) {
@@ -411,9 +426,15 @@ class GPBUtil
$value->setNanos($nanoseconds);
return $value;
}
-
+
public static function formatTimestamp($value)
{
+ if (bccomp($value->getSeconds(), "253402300800") != -1) {
+ throw new GPBDecodeException("Duration number too large.");
+ }
+ if (bccomp($value->getSeconds(), "-62135596801") != 1) {
+ throw new GPBDecodeException("Duration number too small.");
+ }
$nanoseconds = static::getNanosecondsForTimestamp($value->getNanos());
if (!empty($nanoseconds)) {
$nanoseconds = ".".$nanoseconds;
@@ -422,6 +443,93 @@ class GPBUtil
return $date->format("Y-m-d\TH:i:s".$nanoseconds."\Z");
}
+ public static function parseDuration($value)
+ {
+ if (strlen($value) < 2 || substr($value, -1) !== "s") {
+ throw new GPBDecodeException("Missing s after duration string");
+ }
+ $number = substr($value, 0, -1);
+ if (bccomp($number, "315576000001") != -1) {
+ throw new GPBDecodeException("Duration number too large.");
+ }
+ if (bccomp($number, "-315576000001") != 1) {
+ throw new GPBDecodeException("Duration number too small.");
+ }
+ $pos = strrpos($number, ".");
+ if ($pos !== false) {
+ $seconds = substr($number, 0, $pos);
+ if (bccomp($seconds, 0) < 0) {
+ $nanos = bcmul("0" . substr($number, $pos), -1000000000);
+ } else {
+ $nanos = bcmul("0" . substr($number, $pos), 1000000000);
+ }
+ } else {
+ $seconds = $number;
+ $nanos = 0;
+ }
+ $duration = new Duration();
+ $duration->setSeconds($seconds);
+ $duration->setNanos($nanos);
+ return $duration;
+ }
+
+ public static function formatDuration($value)
+ {
+ if (bccomp($value->getSeconds(), "315576000001") != -1) {
+ throw new GPBDecodeException("Duration number too large.");
+ }
+ if (bccomp($value->getSeconds(), "-315576000001") != 1) {
+ throw new GPBDecodeException("Duration number too small.");
+ }
+ return strval(bcadd($value->getSeconds(),
+ $value->getNanos() / 1000000000.0, 9));
+ }
+
+
+
+ public static function parseFieldMask($paths_string)
+ {
+ $path_strings = explode(",", $paths_string);
+ $field_mask = new FieldMask();
+ $paths = $field_mask->getPaths();
+ foreach($path_strings as &$path_string) {
+ $field_strings = explode(".", $path_string);
+ foreach($field_strings as &$field_string) {
+ $field_string = camel2underscore($field_string);
+ }
+ $path_string = implode(".", $field_strings);
+ $paths[] = $path_string;
+ }
+ return $field_mask;
+ }
+
+ public static function formatFieldMask($field_mask)
+ {
+ $converted_paths = [];
+ foreach($field_mask->getPaths() as $path) {
+ $fields = explode('.', $path);
+ $converted_path = [];
+ foreach ($fields as $field) {
+ $segments = explode('_', $field);
+ $start = true;
+ $converted_segments = "";
+ foreach($segments as $segment) {
+ if (!$start) {
+ $converted = ucfirst($segment);
+ } else {
+ $converted = $segment;
+ $start = false;
+ }
+ $converted_segments .= $converted;
+ }
+ $converted_path []= $converted_segments;
+ }
+ $converted_path = implode(".", $converted_path);
+ $converted_paths []= $converted_path;
+ }
+ return implode(",", $converted_paths);
+ }
+
public static function getNanosecondsForTimestamp($nanoseconds)
{
if ($nanoseconds == 0) {
@@ -435,4 +543,29 @@ class GPBUtil
}
return sprintf('%09d', $nanoseconds);
}
+
+ public static function hasSpecialJsonMapping($msg)
+ {
+ return is_a($msg, 'Google\Protobuf\Any') ||
+ is_a($msg, "Google\Protobuf\ListValue") ||
+ is_a($msg, "Google\Protobuf\Struct") ||
+ is_a($msg, "Google\Protobuf\Value") ||
+ is_a($msg, "Google\Protobuf\Duration") ||
+ is_a($msg, "Google\Protobuf\Timestamp") ||
+ is_a($msg, "Google\Protobuf\FieldMask") ||
+ static::hasJsonValue($msg);
+ }
+
+ public static function hasJsonValue($msg)
+ {
+ return is_a($msg, "Google\Protobuf\DoubleValue") ||
+ is_a($msg, "Google\Protobuf\FloatValue") ||
+ is_a($msg, "Google\Protobuf\Int64Value") ||
+ is_a($msg, "Google\Protobuf\UInt64Value") ||
+ is_a($msg, "Google\Protobuf\Int32Value") ||
+ is_a($msg, "Google\Protobuf\UInt32Value") ||
+ is_a($msg, "Google\Protobuf\BoolValue") ||
+ is_a($msg, "Google\Protobuf\StringValue") ||
+ is_a($msg, "Google\Protobuf\BytesValue");
+ }
}
diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php
index b3c4e6f1..9785be30 100644
--- a/php/src/Google/Protobuf/Internal/Message.php
+++ b/php/src/Google/Protobuf/Internal/Message.php
@@ -44,6 +44,10 @@ use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\GPBWire;
use Google\Protobuf\Internal\MapEntry;
use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\ListValue;
+use Google\Protobuf\Value;
+use Google\Protobuf\Struct;
+use Google\Protobuf\NullValue;
/**
* Parent class of all proto messages. Users should not instantiate this class
@@ -701,16 +705,22 @@ class Message
$field,
$is_map_key = false)
{
- if (is_null($value)) {
- return $this->defaultValue($field);
- }
switch ($field->getType()) {
case GPBType::MESSAGE:
$klass = $field->getMessageType()->getClass();
$submsg = new $klass;
- if ($field->isTimestamp()) {
- if (!is_string($value)) {
+ if (is_a($submsg, "Google\Protobuf\Duration")) {
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ } else if (!is_string($value)) {
+ throw new GPBDecodeException("Expect string.");
+ }
+ return GPBUtil::parseDuration($value);
+ } else if ($field->isTimestamp()) {
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ } else if (!is_string($value)) {
throw new GPBDecodeException("Expect string.");
}
try {
@@ -721,16 +731,31 @@ class Message
$submsg->setSeconds($timestamp->getSeconds());
$submsg->setNanos($timestamp->getNanos());
- } else if ($klass !== "Google\Protobuf\Any") {
- if (!is_object($value) && !is_array($value)) {
+ } else if (is_a($submsg, "Google\Protobuf\FieldMask")) {
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
+ try {
+ return GPBUtil::parseFieldMask($value);
+ } catch (\Exception $e) {
+ throw new GPBDecodeException("Invalid FieldMask: ".$e->getMessage());
+ }
+ } else {
+ if (is_null($value) &&
+ !is_a($submsg, "Google\Protobuf\Value")) {
+ return $this->defaultValue($field);
+ }
+ if (GPBUtil::hasSpecialJsonMapping($submsg)) {
+ } elseif (!is_object($value) && !is_array($value)) {
throw new GPBDecodeException("Expect message.");
}
-
$submsg->mergeFromJsonArray($value);
}
return $submsg;
case GPBType::ENUM:
- if (is_integer($value)) {
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ } else if (is_integer($value)) {
return $value;
} else {
$enum_value =
@@ -740,11 +765,17 @@ class Message
return $enum_value->getNumber();
}
case GPBType::STRING:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_string($value)) {
throw new GPBDecodeException("Expect string");
}
return $value;
case GPBType::BYTES:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_string($value)) {
throw new GPBDecodeException("Expect string");
}
@@ -755,6 +786,9 @@ class Message
}
return $proto_value;
case GPBType::BOOL:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if ($is_map_key) {
if ($value === "true") {
return true;
@@ -771,6 +805,9 @@ class Message
}
return $value;
case GPBType::FLOAT:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if ($value === "Infinity") {
return INF;
}
@@ -782,6 +819,9 @@ class Message
}
return $value;
case GPBType::DOUBLE:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if ($value === "Infinity") {
return INF;
}
@@ -793,6 +833,9 @@ class Message
}
return $value;
case GPBType::INT32:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_numeric($value)) {
throw new GPBDecodeException(
"Invalid data type for int32 field");
@@ -807,6 +850,9 @@ class Message
}
return $value;
case GPBType::UINT32:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_numeric($value)) {
throw new GPBDecodeException(
"Invalid data type for uint32 field");
@@ -817,6 +863,9 @@ class Message
}
return $value;
case GPBType::INT64:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_numeric($value)) {
throw new GPBDecodeException(
"Invalid data type for int64 field");
@@ -831,6 +880,9 @@ class Message
}
return $value;
case GPBType::UINT64:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
if (!is_numeric($value)) {
throw new GPBDecodeException(
"Invalid data type for int64 field");
@@ -844,14 +896,107 @@ class Message
}
return $value;
case GPBType::FIXED64:
+ if (is_null($value)) {
+ return $this->defaultValue($field);
+ }
return $value;
default:
return $value;
}
}
- private function mergeFromJsonArray($array)
+ protected function mergeFromJsonArray($array)
{
+ if (is_a($this, "Google\Protobuf\Any")) {
+ $this->clear();
+ $this->setTypeUrl($array["@type"]);
+ $msg = $this->unpack();
+ if (GPBUtil::hasSpecialJsonMapping($msg)) {
+ $msg->mergeFromJsonArray($array["value"]);
+ } else {
+ unset($array["@type"]);
+ $msg->mergeFromJsonArray($array);
+ }
+ $this->setValue($msg->serializeToString());
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\DoubleValue") ||
+ is_a($this, "Google\Protobuf\FloatValue") ||
+ is_a($this, "Google\Protobuf\Int64Value") ||
+ is_a($this, "Google\Protobuf\UInt64Value") ||
+ is_a($this, "Google\Protobuf\Int32Value") ||
+ is_a($this, "Google\Protobuf\UInt32Value") ||
+ is_a($this, "Google\Protobuf\BoolValue") ||
+ is_a($this, "Google\Protobuf\StringValue")) {
+ $this->setValue($array);
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\BytesValue")) {
+ $this->setValue(base64_decode($array));
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\Duration")) {
+ $this->mergeFrom(GPBUtil::parseDuration($array));
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\FieldMask")) {
+ $this->mergeFrom(GPBUtil::parseFieldMask($array));
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\Timestamp")) {
+ $this->mergeFrom(GPBUtil::parseTimestamp($array));
+ return;
+ }
+ if (is_a($this, "Google\Protobuf\Struct")) {
+ $fields = $this->getFields();
+ foreach($array as $key => $value) {
+ $v = new Value();
+ $v->mergeFromJsonArray($value);
+ $fields[$key] = $v;
+ }
+ }
+ if (is_a($this, "Google\Protobuf\Value")) {
+ if (is_bool($array)) {
+ $this->setBoolValue($array);
+ } elseif (is_string($array)) {
+ $this->setStringValue($array);
+ } elseif (is_null($array)) {
+ $this->setNullValue(0);
+ } elseif (is_double($array) || is_integer($array)) {
+ $this->setNumberValue($array);
+ } elseif (is_array($array)) {
+ if (array_values($array) !== $array) {
+ // Associative array
+ $struct_value = $this->getStructValue();
+ if (is_null($struct_value)) {
+ $struct_value = new Struct();
+ $this->setStructValue($struct_value);
+ }
+ foreach ($array as $key => $v) {
+ $value = new Value();
+ $value->mergeFromJsonArray($v);
+ $values = $struct_value->getFields();
+ $values[$key]= $value;
+ }
+ } else {
+ // Array
+ $list_value = $this->getListValue();
+ if (is_null($list_value)) {
+ $list_value = new ListValue();
+ $this->setListValue($list_value);
+ }
+ foreach ($array as $v) {
+ $value = new Value();
+ $value->mergeFromJsonArray($v);
+ $values = $list_value->getValues();
+ $values[]= $value;
+ }
+ }
+ } else {
+ throw new GPBDecodeException("Invalid type for Value.");
+ }
+ return;
+ }
foreach ($array as $key => $value) {
$field = $this->desc->getFieldByJsonName($key);
if (is_null($field)) {
@@ -1037,7 +1182,8 @@ class Message
{
$getter = $field->getGetter();
$values = $this->$getter();
- return GPBJsonWire::serializeFieldToStream($values, $field, $output);
+ return GPBJsonWire::serializeFieldToStream(
+ $values, $field, $output, !GPBUtil::hasSpecialJsonMapping($this));
}
/**
@@ -1060,16 +1206,57 @@ class Message
*/
public function serializeToJsonStream(&$output)
{
- if (get_class($this) === 'Google\Protobuf\Timestamp') {
+ if (is_a($this, 'Google\Protobuf\Any')) {
+ $output->writeRaw("{", 1);
+ $type_field = $this->desc->getFieldByNumber(1);
+ $value_msg = $this->unpack();
+
+ // Serialize type url.
+ $output->writeRaw("\"@type\":", 8);
+ $output->writeRaw("\"", 1);
+ $output->writeRaw($this->getTypeUrl(), strlen($this->getTypeUrl()));
+ $output->writeRaw("\"", 1);
+
+ // Serialize value
+ if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
+ $output->writeRaw(",\"value\":", 9);
+ $value_msg->serializeToJsonStream($output);
+ } else {
+ $value_fields = $value_msg->desc->getField();
+ foreach ($value_fields as $field) {
+ if ($value_msg->existField($field)) {
+ $output->writeRaw(",", 1);
+ if (!$value_msg->serializeFieldToJsonStream($output, $field)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ $output->writeRaw("}", 1);
+ } elseif (is_a($this, 'Google\Protobuf\FieldMask')) {
+ $field_mask = GPBUtil::formatFieldMask($this);
+ $output->writeRaw("\"", 1);
+ $output->writeRaw($field_mask, strlen($field_mask));
+ $output->writeRaw("\"", 1);
+ } elseif (is_a($this, 'Google\Protobuf\Duration')) {
+ $duration = GPBUtil::formatDuration($this) . "s";
+ $output->writeRaw("\"", 1);
+ $output->writeRaw($duration, strlen($duration));
+ $output->writeRaw("\"", 1);
+ } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
$timestamp = GPBUtil::formatTimestamp($this);
$timestamp = json_encode($timestamp);
$output->writeRaw($timestamp, strlen($timestamp));
} else {
- $output->writeRaw("{", 1);
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ $output->writeRaw("{", 1);
+ }
$fields = $this->desc->getField();
$first = true;
foreach ($fields as $field) {
- if ($this->existField($field)) {
+ if ($this->existField($field) ||
+ GPBUtil::hasJsonValue($this)) {
if ($first) {
$first = false;
} else {
@@ -1080,7 +1267,9 @@ class Message
}
}
}
- $output->writeRaw("}", 1);
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ $output->writeRaw("}", 1);
+ }
}
return true;
}
@@ -1263,6 +1452,10 @@ class Message
break;
case GPBType::ENUM:
$enum_desc = $field->getEnumType();
+ if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
+ $size += 4;
+ break;
+ }
$enum_value_desc = $enum_desc->getValueByNumber($value);
if (!is_null($enum_value_desc)) {
$size += 2; // size for ""
@@ -1284,6 +1477,12 @@ class Message
$size += strlen($value);
break;
case GPBType::BYTES:
+ # if (is_a($this, "Google\Protobuf\BytesValue")) {
+ # $size += strlen(json_encode($value));
+ # } else {
+ # $size += strlen(base64_encode($value));
+ # $size += 2; // size for \"\"
+ # }
$size += strlen(base64_encode($value));
$size += 2; // size for \"\"
break;
@@ -1375,8 +1574,11 @@ class Message
$values = $this->$getter();
$count = count($values);
if ($count !== 0) {
- $size += 5; // size for "\"\":{}".
- $size += strlen($field->getJsonName()); // size for field name
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ $size += 3; // size for "\"\":".
+ $size += strlen($field->getJsonName()); // size for field name
+ }
+ $size += 2; // size for "{}".
$size += $count - 1; // size for commas
$getter = $field->getGetter();
$map_entry = $field->getMessageType();
@@ -1408,17 +1610,22 @@ class Message
$values = $this->$getter();
$count = count($values);
if ($count !== 0) {
- $size += 5; // size for "\"\":[]".
- $size += strlen($field->getJsonName()); // size for field name
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ $size += 3; // size for "\"\":".
+ $size += strlen($field->getJsonName()); // size for field name
+ }
+ $size += 2; // size for "[]".
$size += $count - 1; // size for commas
$getter = $field->getGetter();
foreach ($values as $value) {
$size += $this->fieldDataOnlyJsonByteSize($field, $value);
}
}
- } elseif ($this->existField($field)) {
- $size += 3; // size for "\"\":".
- $size += strlen($field->getJsonName()); // size for field name
+ } elseif ($this->existField($field) || GPBUtil::hasJsonValue($this)) {
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ $size += 3; // size for "\"\":".
+ $size += strlen($field->getJsonName()); // size for field name
+ }
$getter = $field->getGetter();
$value = $this->$getter();
$size += $this->fieldDataOnlyJsonByteSize($field, $value);
@@ -1473,14 +1680,41 @@ class Message
public function jsonByteSize()
{
$size = 0;
- if (get_class($this) === 'Google\Protobuf\Timestamp') {
+ if (is_a($this, 'Google\Protobuf\Any')) {
+ // Size for "{}".
+ $size += 2;
+
+ // Size for "\"@type\":".
+ $size += 8;
+
+ // Size for url. +2 for "" /.
+ $size += strlen($this->getTypeUrl()) + 2;
+
+ $value_msg = $this->unpack();
+ if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
+ // Size for "\",value\":".
+ $size += 9;
+ $size += $value_msg->jsonByteSize();
+ } else {
+ // Size for value. +1 for comma, -2 for "{}".
+ $size += $value_msg->jsonByteSize() -1;
+ }
+ } elseif (get_class($this) === 'Google\Protobuf\FieldMask') {
+ $field_mask = GPBUtil::formatFieldMask($this);
+ $size += strlen($field_mask) + 2; // 2 for ""
+ } elseif (get_class($this) === 'Google\Protobuf\Duration') {
+ $duration = GPBUtil::formatDuration($this) . "s";
+ $size += strlen($duration) + 2; // 2 for ""
+ } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
$timestamp = GPBUtil::formatTimestamp($this);
$timestamp = json_encode($timestamp);
$size += strlen($timestamp);
} else {
- // Size for "{}".
- $size += 2;
-
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
+ // Size for "{}".
+ $size += 2;
+ }
+
$fields = $this->desc->getField();
$count = 0;
foreach ($fields as $field) {