|
@@ -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) {
|