From 2df472690ec878ff75a2ccea0c7ff6df0ff69ee3 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Sat, 4 Nov 2017 09:33:56 -0700 Subject: Fix php well known type conformance tests (#3828) (#3840) * 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 --- php/src/Google/Protobuf/Internal/Message.php | 286 ++++++++++++++++++++++++--- 1 file changed, 260 insertions(+), 26 deletions(-) (limited to 'php/src/Google/Protobuf/Internal/Message.php') 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) { -- cgit v1.2.3