PHP namespaces for nested messages and enums (#4536)

* uses namespaces for nested messages and enums

* fixes namespaces for PHP dist

* fixes namespace for Descriptors, adds Cardinality and Kind

* fixes nested namespaces for reserved words and adds tests

* adds tests and generator fix for php class prefixes

* fixes escaping of protobuf packages, enum comments, misc others

* nice refactor of generated code

* adds class files for backwards compatibility

* simplifies code with templates

* adds compatibility files to makefile

* cleanup of generator and fixes nested namespace bug

* regenerates proto types

* remove internal BC classes

* adds deprecated warning, adds methods back

* simplifies if statement

* fixes dist files

* addresses review comments

* adds back TYPE_URL_PREFIX constant

* adds @deprecated to old nested class files

* skips tests which require a separate process when protobuf.so is enabled

* Adds tests for legacy nested classes that do not require separate processes to test

* uses legacy names for GPBUtil message check

* adds block for IDE @deprecated message

* Namespace for nested message/enum in c extension

* Remove unused code
diff --git a/php/tests/generated_class_test.php b/php/tests/generated_class_test.php
index fbee29b..9f20bdd 100644
--- a/php/tests/generated_class_test.php
+++ b/php/tests/generated_class_test.php
@@ -8,14 +8,20 @@
 use Google\Protobuf\Internal\RepeatedField;
 use Google\Protobuf\Internal\MapField;
 use Google\Protobuf\Internal\GPBType;
+use Bar\TestLegacyMessage;
+use Bar\TestLegacyMessage_NestedEnum;
+use Bar\TestLegacyMessage_NestedMessage;
 use Foo\TestEnum;
 use Foo\TestIncludeNamespaceMessage;
 use Foo\TestIncludePrefixMessage;
 use Foo\TestMessage;
+use Foo\TestMessage\Sub;
 use Foo\TestMessage_Sub;
+use Foo\TestMessage\NestedEnum;
 use Foo\TestReverseFieldOrder;
 use Foo\testLowerCaseMessage;
 use Foo\testLowerCaseEnum;
+use PBEmpty\PBEcho\TestEmptyPackage;
 use Php\Test\TestNamespace;
 
 class GeneratedClassTest extends TestBase
@@ -231,9 +237,26 @@
     public function testNestedEnum()
     {
         $m = new TestMessage();
+        $m->setOptionalNestedEnum(NestedEnum::ZERO);
+    }
+
+    public function testLegacyNestedEnum()
+    {
+        $m = new TestMessage();
         $m->setOptionalNestedEnum(\Foo\TestMessage_NestedEnum::ZERO);
     }
 
+    public function testLegacyTypehintWithNestedEnums()
+    {
+        $this->legacyEnum(new TestLegacyMessage\NestedEnum);
+    }
+
+    private function legacyEnum(TestLegacyMessage_NestedEnum $enum)
+    {
+        // If we made it here without a PHP Fatal error, the typehint worked
+        $this->assertTrue(true);
+    }
+
     #########################################################
     # Test float field.
     #########################################################
@@ -370,6 +393,20 @@
     {
         $m = new TestMessage();
 
+        $sub_m = new Sub();
+        $sub_m->setA(1);
+        $m->setOptionalMessage($sub_m);
+        $this->assertSame(1, $m->getOptionalMessage()->getA());
+
+        $null = null;
+        $m->setOptionalMessage($null);
+        $this->assertNull($m->getOptionalMessage());
+    }
+
+    public function testLegacyMessageField()
+    {
+        $m = new TestMessage();
+
         $sub_m = new TestMessage_Sub();
         $sub_m->setA(1);
         $m->setOptionalMessage($sub_m);
@@ -380,6 +417,17 @@
         $this->assertNull($m->getOptionalMessage());
     }
 
+    public function testLegacyTypehintWithNestedMessages()
+    {
+        $this->legacyMessage(new TestLegacyMessage\NestedMessage);
+    }
+
+    private function legacyMessage(TestLegacyMessage_NestedMessage $sub)
+    {
+        // If we made it here without a PHP Fatal error, the typehint worked
+        $this->assertTrue(true);
+    }
+
     #########################################################
     # Test repeated field.
     #########################################################
@@ -474,7 +522,7 @@
         $this->assertSame(NULL, $m->getOneofMessage());
         $this->assertSame("oneof_string", $m->getMyOneof());
 
-        $sub_m = new TestMessage_Sub();
+        $sub_m = new Sub();
         $sub_m->setA(1);
         $m->setOneofMessage($sub_m);
         $this->assertSame(0, $m->getOneofInt32());
@@ -513,7 +561,7 @@
 
         // Singular
         $n->setOptionalInt32(100);
-        $sub1 = new TestMessage_Sub();
+        $sub1 = new Sub();
         $sub1->setA(101);
 
         $b = $sub1->getB();
@@ -531,7 +579,7 @@
         $repeatedString[] = 'abc';
         $n->setRepeatedString($repeatedString);
 
-        $sub2 = new TestMessage_Sub();
+        $sub2 = new Sub();
         $sub2->setA(201);
         $repeatedMessage = $n->getRepeatedMessage();
         $repeatedMessage[] = $sub2;
@@ -548,9 +596,9 @@
         $n->setMapStringString($mapStringString);
 
         $mapInt32Message = $n->getMapInt32Message();
-        $mapInt32Message[1] = new TestMessage_Sub();
+        $mapInt32Message[1] = new Sub();
         $mapInt32Message[1]->setA(302);
-        $mapInt32Message[2] = new TestMessage_Sub();
+        $mapInt32Message[2] = new Sub();
         $mapInt32Message[2]->setA(303);
         $n->setMapInt32Message($mapInt32Message);
 
@@ -607,7 +655,7 @@
         $m->mergeFrom($n);
         $this->assertSame(1, $m->getOneofInt32());
 
-        $sub = new TestMessage_Sub();
+        $sub = new Sub();
         $n->setOneofMessage($sub);
         $n->getOneofMessage()->setA(400);
         $m->mergeFrom($n);
@@ -630,14 +678,15 @@
     public function testMessageWithoutNamespace()
     {
         $m = new TestMessage();
-        $sub = new NoNameSpaceMessage();
-        $m->setOptionalNoNamespaceMessage($sub);
+        $n = new NoNameSpaceMessage();
+        $m->setOptionalNoNamespaceMessage($n);
         $repeatedNoNamespaceMessage = $m->getRepeatedNoNamespaceMessage();
         $repeatedNoNamespaceMessage[] = new NoNameSpaceMessage();
         $m->setRepeatedNoNamespaceMessage($repeatedNoNamespaceMessage);
 
-        $n = new NoNamespaceMessage();
-        $n->setB(NoNamespaceMessage_NestedEnum::ZERO);
+        // test nested messages
+        $sub = new NoNamespaceMessage\NestedMessage();
+        $n->setNestedMessage($sub);
     }
 
     public function testEnumWithoutNamespace()
@@ -650,35 +699,91 @@
     }
 
     #########################################################
-    # Test message with given prefix.
+    # Test message with given namespace.
     #########################################################
 
-    public function testPrefixMessage()
+    public function testNestedMessagesAndEnums()
     {
+        $m = new TestMessage();
+        $n = new TestMessage\Sub();
+        $m->setOptionalMessage($n);
+        $m->setOptionalNestedEnum(TestMessage\NestedEnum::ZERO);
+        $this->assertSame($n, $m->getOptionalMessage());
+        $this->assertSame(TestMessage\NestedEnum::ZERO, $m->getOptionalNestedEnum());
+    }
+
+    public function testMessagesAndEnumsWithPrefix()
+    {
+        // Test message prefix
         $m = new TestIncludePrefixMessage();
         $n = new PrefixTestPrefix();
         $n->setA(1);
         $m->setPrefixMessage($n);
         $this->assertSame(1, $m->getPrefixMessage()->getA());
+
+        // Test nested message prefix
+        $o = new PrefixTestPrefix();
+        $p = new PrefixTestPrefix\PrefixNestedMessage();
+        $o->setNestedMessage($p);
+        $o->setNestedEnum(PrefixTestPrefix\PrefixNestedEnum::ZERO);
+        $this->assertSame($p, $o->getNestedMessage());
+        $this->assertSame(PrefixTestPrefix\PrefixNestedEnum::ZERO, $o->getNestedEnum());
     }
 
-    #########################################################
-    # Test message with given namespace.
-    #########################################################
-
-    public function testNamespaceMessage()
+    public function testMessagesAndEnumsWithPhpNamespace()
     {
-        $m = new TestIncludeNamespaceMessage();
+        $m = new TestNamespace();
+        $n = new TestNamespace\NestedMessage();
+        $m->setNestedMessage($n);
+        $m->setNestedEnum(TestNamespace\NestedEnum::ZERO);
+        $this->assertSame($n, $m->getNestedMessage());
+        $this->assertSame(TestNamespace\NestedEnum::ZERO, $m->getNestedEnum());
+    }
 
-        $n = new TestNamespace();
-        $n->setA(1);
-        $m->setNamespaceMessage($n);
-        $this->assertSame(1, $m->getNamespaceMessage()->getA());
+    public function testMesssagesAndEnumsWithEmptyPhpNamespace()
+    {
+        $m = new TestEmptyNamespace();
+        $n = new TestEmptyNamespace\NestedMessage();
+        $m->setNestedMessage($n);
+        $m->setNestedEnum(TestEmptyNamespace\NestedEnum::ZERO);
+        $this->assertSame($n, $m->getNestedMessage());
+        $this->assertSame(TestEmptyNamespace\NestedEnum::ZERO, $m->getNestedEnum());
+    }
 
-        $n = new TestEmptyNamespace();
-        $n->setA(1);
-        $m->setEmptyNamespaceMessage($n);
-        $this->assertSame(1, $m->getEmptyNamespaceMessage()->getA());
+    public function testMessagesAndEnumsWithNoNamespace()
+    {
+        $m = new NoNamespaceMessage();
+        $n = new NoNamespaceMessage\NestedMessage();
+        $m->setNestedMessage($n);
+        $m->setNestedEnum(NoNamespaceMessage\NestedEnum::ZERO);
+        $this->assertSame($n, $m->getNestedMessage());
+        $this->assertSame(NoNamespaceMessage\NestedEnum::ZERO, $m->getNestedEnum());
+    }
+
+    public function testReservedWordsInPackageName()
+    {
+        $m = new TestEmptyPackage();
+        $n = new TestEmptyPackage\NestedMessage();
+        $m->setNestedMessage($n);
+        $m->setNestedEnum(TestEmptyPackage\NestedEnum::ZERO);
+        $this->assertSame($n, $m->getNestedMessage());
+        $this->assertSame(TestEmptyPackage\NestedEnum::ZERO, $m->getNestedEnum());
+    }
+
+    public function testReservedWordsInNamespace()
+    {
+        $m = new TestNamespace();
+        $n = new TestNamespace\PBEmpty();
+        $o = new TestNamespace\PBEmpty\NestedMessage();
+        $n->setNestedMessage($o);
+        $n->setNestedEnum(TestNamespace\PBEmpty\NestedEnum::ZERO);
+        $m->setReservedName($n);
+        $this->assertSame($n, $m->getReservedName());
+        $this->assertSame($o, $n->getNestedMessage());
+        $this->assertSame(
+            TestNamespace\PBEmpty\NestedEnum::ZERO,
+            $n->getNestedEnum()
+        );
     }
 
     #########################################################
@@ -687,7 +792,7 @@
 
     public function testPrefixForReservedWords()
     {
-        $m = new \Foo\TestMessage_Empty();
+        $m = new \Foo\TestMessage\PBEmpty();
         $m = new \Foo\PBEmpty();
         $m = new \PrefixEmpty();
         $m = new \Foo\PBARRAY();
@@ -1195,7 +1300,7 @@
             'optional_string' => 'a',
             'optional_bytes' => 'b',
             'optional_enum' => TestEnum::ONE,
-            'optional_message' => new TestMessage_Sub([
+            'optional_message' => new Sub([
                 'a' => 33
             ]),
             'repeated_int32' => [-42, -52],
@@ -1214,8 +1319,8 @@
             'repeated_string' => ['a', 'c'],
             'repeated_bytes' => ['b', 'd'],
             'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
-            'repeated_message' => [new TestMessage_Sub(['a' => 34]),
-                                   new TestMessage_Sub(['a' => 35])],
+            'repeated_message' => [new Sub(['a' => 34]),
+                                   new Sub(['a' => 35])],
             'map_int32_int32' => [-62 => -62],
             'map_int64_int64' => [-63 => -63],
             'map_uint32_uint32' => [62 => 62],
@@ -1232,7 +1337,7 @@
             'map_string_string' => ['e' => 'e'],
             'map_int32_bytes' => [1 => 'f'],
             'map_int32_enum' => [1 => TestEnum::ONE],
-            'map_int32_message' => [1 => new TestMessage_Sub(['a' => 36])],
+            'map_int32_message' => [1 => new Sub(['a' => 36])],
         ]);
 
         TestUtil::assertTestMessage($m);