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