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