diff options
26 files changed, 941 insertions, 179 deletions
diff --git a/conformance/Makefile.am b/conformance/Makefile.am index f7ce053b..765f3588 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -256,7 +256,7 @@ if USE_EXTERNAL_PROTOC protoc_middleman: $(conformance_protoc_inputs) $(conformance_proto2_protoc_inputs) $(well_known_type_protoc_inputs) google-protobuf $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. --python_out=. --php_out=. --js_out=import_style=commonjs,binary:. $(conformance_protoc_inputs) $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --objc_out=. --python_out=. --js_out=import_style=commonjs,binary:. $(conformance_proto2_protoc_inputs) - $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. --php_out=. --js_out=import_style=commonjs,binary:google-protobuf $(well_known_type_protoc_inputs) + $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. --js_out=import_style=commonjs,binary:google-protobuf $(well_known_type_protoc_inputs) ## $(PROTOC) -I$(srcdir) -I$(top_srcdir) --java_out=lite:lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) touch protoc_middleman @@ -268,7 +268,7 @@ else protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(conformance_protoc_inputs) $(conformance_proto2_protoc_inputs) $(well_known_type_protoc_inputs) google-protobuf oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd --python_out=$$oldpwd --php_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd $(conformance_protoc_inputs) ) oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --objc_out=. --python_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd $(conformance_proto2_protoc_inputs) ) - oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd --php_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd/google-protobuf $(well_known_type_protoc_inputs) ) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd --js_out=import_style=commonjs,binary:$$oldpwd/google-protobuf $(well_known_type_protoc_inputs) ) ## @mkdir -p lite ## oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --java_out=lite:$$oldpwd/lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) ) touch protoc_middleman diff --git a/conformance/autoload.php b/conformance/autoload.php index 2cee31c4..0f49aecb 100644 --- a/conformance/autoload.php +++ b/conformance/autoload.php @@ -2,7 +2,7 @@ define("GOOGLE_INTERNAL_NAMESPACE", "Google\\Protobuf\\Internal\\"); define("GOOGLE_NAMESPACE", "Google\\Protobuf\\"); -define("GOOGLE_GPBMETADATA_NAMESPACE", "GPBMetadata\\Google\\Protobuf\\Internal\\"); +define("GOOGLE_GPBMETADATA_NAMESPACE", "GPBMetadata\\Google\\Protobuf\\"); function protobuf_autoloader_impl($class, $prefix) { $length = strlen($prefix); diff --git a/conformance/conformance_php.php b/conformance/conformance_php.php index 4dc18f59..19f9a092 100755 --- a/conformance/conformance_php.php +++ b/conformance/conformance_php.php @@ -3,24 +3,6 @@ require_once("Conformance/WireFormat.php"); require_once("Conformance/ConformanceResponse.php"); require_once("Conformance/ConformanceRequest.php"); -require_once("Google/Protobuf/Any.php"); -require_once("Google/Protobuf/Duration.php"); -require_once("Google/Protobuf/FieldMask.php"); -require_once("Google/Protobuf/Struct.php"); -require_once("Google/Protobuf/Value.php"); -require_once("Google/Protobuf/ListValue.php"); -require_once("Google/Protobuf/NullValue.php"); -require_once("Google/Protobuf/Timestamp.php"); -require_once("Google/Protobuf/DoubleValue.php"); -require_once("Google/Protobuf/BytesValue.php"); -require_once("Google/Protobuf/FloatValue.php"); -require_once("Google/Protobuf/Int64Value.php"); -require_once("Google/Protobuf/UInt32Value.php"); -require_once("Google/Protobuf/BoolValue.php"); -require_once("Google/Protobuf/DoubleValue.php"); -require_once("Google/Protobuf/Int32Value.php"); -require_once("Google/Protobuf/StringValue.php"); -require_once("Google/Protobuf/UInt64Value.php"); require_once("Protobuf_test_messages/Proto3/ForeignMessage.php"); require_once("Protobuf_test_messages/Proto3/ForeignEnum.php"); require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3.php"); @@ -28,12 +10,6 @@ require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3_NestedMessage.php require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3_NestedEnum.php"); require_once("GPBMetadata/Conformance.php"); -require_once("GPBMetadata/Google/Protobuf/Any.php"); -require_once("GPBMetadata/Google/Protobuf/Duration.php"); -require_once("GPBMetadata/Google/Protobuf/FieldMask.php"); -require_once("GPBMetadata/Google/Protobuf/Struct.php"); -require_once("GPBMetadata/Google/Protobuf/Timestamp.php"); -require_once("GPBMetadata/Google/Protobuf/Wrappers.php"); require_once("GPBMetadata/Google/Protobuf/TestMessagesProto3.php"); use \Conformance\WireFormat; @@ -78,7 +54,12 @@ function doTest($request) } elseif ($request->getRequestedOutputFormat() == WireFormat::PROTOBUF) { $response->setProtobufPayload($test_message->serializeToString()); } elseif ($request->getRequestedOutputFormat() == WireFormat::JSON) { - $response->setJsonPayload($test_message->serializeToJsonString()); + try { + $response->setJsonPayload($test_message->serializeToJsonString()); + } catch (Exception $e) { + $response->setSerializeError($e->getMessage()); + return $response; + } } return $response; diff --git a/conformance/failure_list_php.txt b/conformance/failure_list_php.txt index c1713cb1..0d234112 100644 --- a/conformance/failure_list_php.txt +++ b/conformance/failure_list_php.txt @@ -7,92 +7,7 @@ Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator Recommended.Proto3.JsonInput.DurationHasZeroFractionalDigit.Validator -Required.DurationProtoInputTooLarge.JsonOutput -Required.DurationProtoInputTooSmall.JsonOutput -Required.Proto3.JsonInput.Any.JsonOutput -Required.Proto3.JsonInput.Any.ProtobufOutput -Required.Proto3.JsonInput.AnyNested.JsonOutput -Required.Proto3.JsonInput.AnyNested.ProtobufOutput -Required.Proto3.JsonInput.AnyUnorderedTypeTag.JsonOutput -Required.Proto3.JsonInput.AnyUnorderedTypeTag.ProtobufOutput -Required.Proto3.JsonInput.AnyWithDuration.JsonOutput -Required.Proto3.JsonInput.AnyWithDuration.ProtobufOutput -Required.Proto3.JsonInput.AnyWithFieldMask.JsonOutput -Required.Proto3.JsonInput.AnyWithFieldMask.ProtobufOutput -Required.Proto3.JsonInput.AnyWithInt32ValueWrapper.JsonOutput -Required.Proto3.JsonInput.AnyWithInt32ValueWrapper.ProtobufOutput -Required.Proto3.JsonInput.AnyWithStruct.JsonOutput -Required.Proto3.JsonInput.AnyWithStruct.ProtobufOutput -Required.Proto3.JsonInput.AnyWithTimestamp.JsonOutput -Required.Proto3.JsonInput.AnyWithTimestamp.ProtobufOutput -Required.Proto3.JsonInput.AnyWithValueForInteger.JsonOutput -Required.Proto3.JsonInput.AnyWithValueForInteger.ProtobufOutput -Required.Proto3.JsonInput.AnyWithValueForJsonObject.JsonOutput -Required.Proto3.JsonInput.AnyWithValueForJsonObject.ProtobufOutput -Required.Proto3.JsonInput.DurationMaxValue.JsonOutput -Required.Proto3.JsonInput.DurationMaxValue.ProtobufOutput -Required.Proto3.JsonInput.DurationMinValue.JsonOutput -Required.Proto3.JsonInput.DurationMinValue.ProtobufOutput -Required.Proto3.JsonInput.DurationRepeatedValue.JsonOutput -Required.Proto3.JsonInput.DurationRepeatedValue.ProtobufOutput -Required.Proto3.JsonInput.FieldMask.JsonOutput -Required.Proto3.JsonInput.FieldMask.ProtobufOutput -Required.Proto3.JsonInput.OptionalBoolWrapper.JsonOutput -Required.Proto3.JsonInput.OptionalBoolWrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalBytesWrapper.JsonOutput -Required.Proto3.JsonInput.OptionalBytesWrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalDoubleWrapper.JsonOutput -Required.Proto3.JsonInput.OptionalDoubleWrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalFloatWrapper.JsonOutput -Required.Proto3.JsonInput.OptionalFloatWrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalInt32Wrapper.JsonOutput -Required.Proto3.JsonInput.OptionalInt32Wrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalInt64Wrapper.JsonOutput -Required.Proto3.JsonInput.OptionalInt64Wrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalStringWrapper.JsonOutput -Required.Proto3.JsonInput.OptionalStringWrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalUint32Wrapper.JsonOutput -Required.Proto3.JsonInput.OptionalUint32Wrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalUint64Wrapper.JsonOutput -Required.Proto3.JsonInput.OptionalUint64Wrapper.ProtobufOutput -Required.Proto3.JsonInput.OptionalWrapperTypesWithNonDefaultValue.JsonOutput -Required.Proto3.JsonInput.OptionalWrapperTypesWithNonDefaultValue.ProtobufOutput -Required.Proto3.JsonInput.RepeatedBoolWrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedBoolWrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedBytesWrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedBytesWrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedDoubleWrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedDoubleWrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedFloatWrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedFloatWrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedInt32Wrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedInt32Wrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedInt64Wrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedInt64Wrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedStringWrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedStringWrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedUint32Wrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedUint32Wrapper.ProtobufOutput -Required.Proto3.JsonInput.RepeatedUint64Wrapper.JsonOutput -Required.Proto3.JsonInput.RepeatedUint64Wrapper.ProtobufOutput -Required.Proto3.JsonInput.Struct.JsonOutput -Required.Proto3.JsonInput.Struct.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptBool.JsonOutput -Required.Proto3.JsonInput.ValueAcceptBool.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptFloat.JsonOutput -Required.Proto3.JsonInput.ValueAcceptFloat.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput -Required.Proto3.JsonInput.ValueAcceptInteger.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptList.JsonOutput -Required.Proto3.JsonInput.ValueAcceptList.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptNull.JsonOutput -Required.Proto3.JsonInput.ValueAcceptNull.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput -Required.Proto3.JsonInput.ValueAcceptObject.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptString.JsonOutput -Required.Proto3.JsonInput.ValueAcceptString.ProtobufOutput -Required.TimestampProtoInputTooLarge.JsonOutput -Required.TimestampProtoInputTooSmall.JsonOutput +Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter Required.Proto3.JsonInput.FloatFieldTooLarge Required.Proto3.JsonInput.FloatFieldTooSmall Required.Proto3.JsonInput.DoubleFieldTooSmall diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto index 6c9f7634..332c81dc 100644 --- a/csharp/protos/unittest_issues.proto +++ b/csharp/protos/unittest_issues.proto @@ -124,3 +124,18 @@ message TestJsonName { string description = 2 [json_name = "desc"]; string guid = 3 [json_name = "exid"]; } + +// Issue 3200: When merging two messages which use the same +// oneof case, which is itself a message type, the submessages should +// be merged. +message OneofMerging { + message Nested { + int32 x = 1; + int32 y = 2; + } + + oneof value { + string text = 1; + Nested nested = 2; + } +}
\ No newline at end of file diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs index a38d6b08..2caf80a9 100644 --- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs +++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs @@ -33,7 +33,7 @@ using Google.Protobuf.Reflection;
using UnitTest.Issues.TestProtos;
using NUnit.Framework;
-
+using static UnitTest.Issues.TestProtos.OneofMerging.Types;
namespace Google.Protobuf
{
@@ -78,5 +78,17 @@ namespace Google.Protobuf Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }",
JsonFormatter.Default.Format(message));
}
+
+ [Test]
+ public void OneofMerging()
+ {
+ var message1 = new OneofMerging { Nested = new Nested { X = 10 } };
+ var message2 = new OneofMerging { Nested = new Nested { Y = 20 } };
+ var expected = new OneofMerging { Nested = new Nested { X = 10, Y = 20 } };
+
+ var merged = message1.Clone();
+ merged.MergeFrom(message2);
+ Assert.AreEqual(expected, merged);
+ }
}
}
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs index 7c0ba8a6..cbf7909c 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs @@ -42,11 +42,14 @@ namespace UnitTest.Issues.TestProtos { "MV9pbnQzMhgFIAEoBUgAEhQKDHBsYWluX3N0cmluZxgBIAEoCRISCghvMl9p", "bnQzMhgGIAEoBUgBEhMKCW8yX3N0cmluZxgDIAEoCUgBQgQKAm8xQgQKAm8y", "IksKDFRlc3RKc29uTmFtZRIMCgRuYW1lGAEgASgJEhkKC2Rlc2NyaXB0aW9u", - "GAIgASgJUgRkZXNjEhIKBGd1aWQYAyABKAlSBGV4aWQqVQoMTmVnYXRpdmVF", - "bnVtEhYKEk5FR0FUSVZFX0VOVU1fWkVSTxAAEhYKCUZpdmVCZWxvdxD7////", - "//////8BEhUKCE1pbnVzT25lEP///////////wEqLgoORGVwcmVjYXRlZEVu", - "dW0SEwoPREVQUkVDQVRFRF9aRVJPEAASBwoDb25lEAFCH0gBqgIaVW5pdFRl", - "c3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw==")); + "GAIgASgJUgRkZXNjEhIKBGd1aWQYAyABKAlSBGV4aWQifwoMT25lb2ZNZXJn", + "aW5nEg4KBHRleHQYASABKAlIABI2CgZuZXN0ZWQYAiABKAsyJC51bml0dGVz", + "dF9pc3N1ZXMuT25lb2ZNZXJnaW5nLk5lc3RlZEgAGh4KBk5lc3RlZBIJCgF4", + "GAEgASgFEgkKAXkYAiABKAVCBwoFdmFsdWUqVQoMTmVnYXRpdmVFbnVtEhYK", + "Ek5FR0FUSVZFX0VOVU1fWkVSTxAAEhYKCUZpdmVCZWxvdxD7//////////8B", + "EhUKCE1pbnVzT25lEP///////////wEqLgoORGVwcmVjYXRlZEVudW0SEwoP", + "REVQUkVDQVRFRF9aRVJPEAASBwoDb25lEAFCH0gBqgIaVW5pdFRlc3QuSXNz", + "dWVzLlRlc3RQcm90b3NiBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedClrTypeInfo[] { @@ -57,7 +60,8 @@ namespace UnitTest.Issues.TestProtos { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging), global::UnitTest.Issues.TestProtos.OneofMerging.Parser, new[]{ "Text", "Nested" }, new[]{ "Value" }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested), global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested.Parser, new[]{ "X", "Y" }, null, null, null)}) })); } #endregion @@ -1729,6 +1733,347 @@ namespace UnitTest.Issues.TestProtos { } + /// <summary> + /// Issue 3200: When merging two messages which use the same + /// oneof case, which is itself a message type, the submessages should + /// be merged. + /// </summary> + public sealed partial class OneofMerging : pb::IMessage<OneofMerging> { + private static readonly pb::MessageParser<OneofMerging> _parser = new pb::MessageParser<OneofMerging>(() => new OneofMerging()); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser<OneofMerging> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[8]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public OneofMerging() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public OneofMerging(OneofMerging other) : this() { + switch (other.ValueCase) { + case ValueOneofCase.Text: + Text = other.Text; + break; + case ValueOneofCase.Nested: + Nested = other.Nested.Clone(); + break; + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public OneofMerging Clone() { + return new OneofMerging(this); + } + + /// <summary>Field number for the "text" field.</summary> + public const int TextFieldNumber = 1; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Text { + get { return valueCase_ == ValueOneofCase.Text ? (string) value_ : ""; } + set { + value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + valueCase_ = ValueOneofCase.Text; + } + } + + /// <summary>Field number for the "nested" field.</summary> + public const int NestedFieldNumber = 2; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested Nested { + get { return valueCase_ == ValueOneofCase.Nested ? (global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested) value_ : null; } + set { + value_ = value; + valueCase_ = value == null ? ValueOneofCase.None : ValueOneofCase.Nested; + } + } + + private object value_; + /// <summary>Enum of possible cases for the "value" oneof.</summary> + public enum ValueOneofCase { + None = 0, + Text = 1, + Nested = 2, + } + private ValueOneofCase valueCase_ = ValueOneofCase.None; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public ValueOneofCase ValueCase { + get { return valueCase_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearValue() { + valueCase_ = ValueOneofCase.None; + value_ = null; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as OneofMerging); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(OneofMerging other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Text != other.Text) return false; + if (!object.Equals(Nested, other.Nested)) return false; + if (ValueCase != other.ValueCase) return false; + return true; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (valueCase_ == ValueOneofCase.Text) hash ^= Text.GetHashCode(); + if (valueCase_ == ValueOneofCase.Nested) hash ^= Nested.GetHashCode(); + hash ^= (int) valueCase_; + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (valueCase_ == ValueOneofCase.Text) { + output.WriteRawTag(10); + output.WriteString(Text); + } + if (valueCase_ == ValueOneofCase.Nested) { + output.WriteRawTag(18); + output.WriteMessage(Nested); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (valueCase_ == ValueOneofCase.Text) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Text); + } + if (valueCase_ == ValueOneofCase.Nested) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Nested); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(OneofMerging other) { + if (other == null) { + return; + } + switch (other.ValueCase) { + case ValueOneofCase.Text: + Text = other.Text; + break; + case ValueOneofCase.Nested: + if (Nested == null) { + Nested = new global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested(); + } + Nested.MergeFrom(other.Nested); + break; + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Text = input.ReadString(); + break; + } + case 18: { + global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested subBuilder = new global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested(); + if (valueCase_ == ValueOneofCase.Nested) { + subBuilder.MergeFrom(Nested); + } + input.ReadMessage(subBuilder); + Nested = subBuilder; + break; + } + } + } + } + + #region Nested types + /// <summary>Container for nested types declared in the OneofMerging message type.</summary> + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static partial class Types { + public sealed partial class Nested : pb::IMessage<Nested> { + private static readonly pb::MessageParser<Nested> _parser = new pb::MessageParser<Nested>(() => new Nested()); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser<Nested> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::UnitTest.Issues.TestProtos.OneofMerging.Descriptor.NestedTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Nested() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Nested(Nested other) : this() { + x_ = other.x_; + y_ = other.y_; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Nested Clone() { + return new Nested(this); + } + + /// <summary>Field number for the "x" field.</summary> + public const int XFieldNumber = 1; + private int x_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int X { + get { return x_; } + set { + x_ = value; + } + } + + /// <summary>Field number for the "y" field.</summary> + public const int YFieldNumber = 2; + private int y_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int Y { + get { return y_; } + set { + y_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Nested); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Nested other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (X != other.X) return false; + if (Y != other.Y) return false; + return true; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (X != 0) hash ^= X.GetHashCode(); + if (Y != 0) hash ^= Y.GetHashCode(); + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (X != 0) { + output.WriteRawTag(8); + output.WriteInt32(X); + } + if (Y != 0) { + output.WriteRawTag(16); + output.WriteInt32(Y); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (X != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(X); + } + if (Y != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Y); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Nested other) { + if (other == null) { + return; + } + if (other.X != 0) { + X = other.X; + } + if (other.Y != 0) { + Y = other.Y; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + X = input.ReadInt32(); + break; + } + case 16: { + Y = input.ReadInt32(); + break; + } + } + } + } + + } + + } + #endregion + + } + #endregion } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs index 1fa35521..65119894 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs @@ -466,10 +466,16 @@ namespace Google.Protobuf.WellKnownTypes { BoolValue = other.BoolValue; break; case KindOneofCase.StructValue: - StructValue = other.StructValue; + if (StructValue == null) { + StructValue = new global::Google.Protobuf.WellKnownTypes.Struct(); + } + StructValue.MergeFrom(other.StructValue); break; case KindOneofCase.ListValue: - ListValue = other.ListValue; + if (ListValue == null) { + ListValue = new global::Google.Protobuf.WellKnownTypes.ListValue(); + } + ListValue.MergeFrom(other.ListValue); break; } diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index 37cff6ce..afe39c1e 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -2591,13 +2591,14 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( if (other == self) { return YES; } - if (![other isKindOfClass:[self class]] && - ![self isKindOfClass:[other class]]) { + if (![other isKindOfClass:[GPBMessage class]]) { return NO; } - GPBMessage *otherMsg = other; GPBDescriptor *descriptor = [[self class] descriptor]; + if ([[otherMsg class] descriptor] != descriptor) { + return NO; + } uint8_t *selfStorage = (uint8_t *)messageStorage_; uint8_t *otherStorage = (uint8_t *)otherMsg->messageStorage_; 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) { diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc index 67c0b596..9ceffa8c 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc @@ -89,6 +89,10 @@ EnumOneofFieldGenerator::EnumOneofFieldGenerator( EnumOneofFieldGenerator::~EnumOneofFieldGenerator() { } +void EnumOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) { + printer->Print(variables_, "$property_name$ = other.$property_name$;\n"); +} + void EnumOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) { // TODO(jonskeet): What about if we read the default value? printer->Print( diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.h b/src/google/protobuf/compiler/csharp/csharp_enum_field.h index 9b7669ba..631632bc 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.h @@ -64,6 +64,7 @@ class EnumOneofFieldGenerator : public PrimitiveOneofFieldGenerator { const Options *options); ~EnumOneofFieldGenerator(); + virtual void GenerateMergingCode(io::Printer* printer); virtual void GenerateParsingCode(io::Printer* printer); virtual void GenerateSerializationCode(io::Printer* printer); virtual void GenerateSerializedSizeCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index 5ef0e4e8..ad9f8a16 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -463,9 +463,12 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { vars["field_property_name"] = GetPropertyName(field); printer->Print( vars, - "case $property_name$OneofCase.$field_property_name$:\n" - " $field_property_name$ = other.$field_property_name$;\n" - " break;\n"); + "case $property_name$OneofCase.$field_property_name$:\n"); + printer->Indent(); + scoped_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field)); + generator->GenerateMergingCode(printer); + printer->Print("break;\n"); + printer->Outdent(); } printer->Outdent(); printer->Print("}\n\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc index 438f3102..59b7edfb 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -171,6 +171,14 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) { "}\n"); } +void MessageOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) { + printer->Print(variables_, + "if ($property_name$ == null) {\n" + " $property_name$ = new $type_name$();\n" + "}\n" + "$property_name$.MergeFrom(other.$property_name$);\n"); +} + void MessageOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) { // TODO(jonskeet): We may be able to do better than this printer->Print( diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.h b/src/google/protobuf/compiler/csharp/csharp_message_field.h index 7d614756..c41ee88a 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.h @@ -74,6 +74,7 @@ class MessageOneofFieldGenerator : public MessageFieldGenerator { virtual void GenerateCloningCode(io::Printer* printer); virtual void GenerateMembers(io::Printer* printer); + virtual void GenerateMergingCode(io::Printer* printer); virtual void WriteToString(io::Printer* printer); virtual void GenerateParsingCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc index 169122e6..931adb4a 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -196,6 +196,10 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) { "}\n"); } +void PrimitiveOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) { + printer->Print(variables_, "$property_name$ = other.$property_name$;\n"); +} + void PrimitiveOneofFieldGenerator::WriteToString(io::Printer* printer) { printer->Print(variables_, "PrintField(\"$descriptor_name$\", $has_property_check$, $oneof_name$_, writer);\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.h b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h index 5f466fc4..ca7b8b3d 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h @@ -78,6 +78,7 @@ class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator { virtual void GenerateCloningCode(io::Printer* printer); virtual void GenerateMembers(io::Printer* printer); + virtual void GenerateMergingCode(io::Printer* printer); virtual void WriteToString(io::Printer* printer); virtual void GenerateParsingCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index 797d498e..997969a0 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -181,6 +181,10 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { "}\n"); } +void WrapperOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) { + printer->Print(variables_, "$property_name$ = other.$property_name$;\n"); +} + void WrapperOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) { printer->Print( variables_, diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h index 250dfd25..452531fb 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h @@ -75,6 +75,7 @@ class WrapperOneofFieldGenerator : public WrapperFieldGenerator { ~WrapperOneofFieldGenerator(); virtual void GenerateMembers(io::Printer* printer); + virtual void GenerateMergingCode(io::Printer* printer); virtual void GenerateParsingCode(io::Printer* printer); virtual void GenerateSerializationCode(io::Printer* printer); virtual void GenerateSerializedSizeCode(io::Printer* printer); @@ -363,7 +363,8 @@ generate_php_test_proto() { proto/test_service_namespace.proto \ proto/test_descriptors.proto pushd ../../src - ./protoc --php_out=../php/tests/generated -I../php/tests -I. ../php/tests/proto/test_import_descriptor_proto.proto + ./protoc --php_out=../php/tests/generated -I../php/tests -I. \ + ../php/tests/proto/test_import_descriptor_proto.proto popd popd } |