THRIFT-5756 Run php tests in github actions
diff --git a/lib/php/test/Unit/BaseValidatorTest.php b/lib/php/test/Unit/BaseValidatorTest.php
new file mode 100644
index 0000000..4404e72
--- /dev/null
+++ b/lib/php/test/Unit/BaseValidatorTest.php
@@ -0,0 +1,168 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use PHPUnit\Framework\TestCase;
+use Thrift\Exception\TProtocolException;
+use Thrift\Protocol\TBinaryProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+abstract class BaseValidatorTest extends TestCase
+{
+    public function testEmptyStructValidator()
+    {
+        $this->assertNoReadValidator('ThriftTest\EmptyStruct');
+        $this->assertNoWriteValidator('ThriftTest\EmptyStruct');
+    }
+
+    public function testBonkValidator()
+    {
+        $this->assertNoReadValidator('ThriftTest\Bonk');
+        $this->assertHasWriteValidator('ThriftTest\Bonk');
+    }
+
+    public function testStructAValidator()
+    {
+        $this->assertHasReadValidator('ThriftTest\StructA');
+        $this->assertHasWriteValidator('ThriftTest\StructA');
+    }
+
+    public function testUnionOfStringsValidator()
+    {
+        $this->assertNoWriteValidator('TestValidators\UnionOfStrings');
+    }
+
+    public function testServiceResultValidator()
+    {
+        $this->assertNoReadValidator('TestValidators\TestService_test_result');
+        $this->assertNoWriteValidator('TestValidators\TestService_test_result');
+    }
+
+    public function testReadEmpty()
+    {
+        $bonk = new \ThriftTest\Bonk();
+        $transport = new TMemoryBuffer("\000");
+        $protocol = new TBinaryProtocol($transport);
+        $bonk->read($protocol);
+        $this->assertTrue(true);
+    }
+
+    public function testWriteEmpty()
+    {
+        $bonk = new \ThriftTest\Bonk();
+        $transport = new TMemoryBuffer();
+        $protocol = new TBinaryProtocol($transport);
+        try {
+            $bonk->write($protocol);
+            $this->fail('Bonk was able to write an empty object');
+        } catch (TProtocolException $e) {
+            $this->expectExceptionMessage('Required field Bonk.message is unset!');
+            throw $e;
+        }
+    }
+
+    public function testWriteWithMissingRequired()
+    {
+        // Check that we are not able to write StructA with a missing required field
+        $structa = new \ThriftTest\StructA();
+        $transport = new TMemoryBuffer();
+        $protocol = new TBinaryProtocol($transport);
+
+        try {
+            $structa->write($protocol);
+            $this->fail('StructA was able to write an empty object');
+        } catch (TProtocolException $e) {
+            $this->expectExceptionMessage('Required field StructA.s is unset!');
+            throw $e;
+        }
+    }
+
+    public function testReadStructA()
+    {
+        $transport = new TMemoryBuffer(base64_decode('CwABAAAAA2FiYwA='));
+        $protocol = new TBinaryProtocol($transport);
+        $structa = new \ThriftTest\StructA();
+        $structa->read($protocol);
+        $this->assertEquals("abc", $structa->s);
+    }
+
+    public function testWriteStructA()
+    {
+        $transport = new TMemoryBuffer();
+        $protocol = new TBinaryProtocol($transport);
+        $structa = new \ThriftTest\StructA();
+        $structa->s = "abc";
+        $structa->write($protocol);
+        $writeResult = base64_encode($transport->getBuffer());
+        $this->assertEquals('CwABAAAAA2FiYwA=', $writeResult);
+    }
+
+    protected static function assertHasReadValidator($class)
+    {
+        if (!static::hasReadValidator($class)) {
+            static::fail($class . ' class should have a read validator');
+        } else {
+            static::assertTrue(true);
+        }
+    }
+
+    protected static function assertNoReadValidator($class)
+    {
+        if (static::hasReadValidator($class)) {
+            static::fail($class . ' class should not have a write validator');
+        } else {
+            static::assertTrue(true);
+        }
+    }
+
+    protected static function assertHasWriteValidator($class)
+    {
+        if (!static::hasWriteValidator($class)) {
+            static::fail($class . ' class should have a write validator');
+        } else {
+            static::assertTrue(true);
+        }
+    }
+
+    protected static function assertNoWriteValidator($class)
+    {
+        if (static::hasWriteValidator($class)) {
+            static::fail($class . ' class should not have a write validator');
+        } else {
+            static::assertTrue(true);
+        }
+    }
+
+    private static function hasReadValidator($class)
+    {
+        $rc = new \ReflectionClass($class);
+
+        return $rc->hasMethod('_validateForRead');
+    }
+
+    private static function hasWriteValidator($class)
+    {
+        $rc = new \ReflectionClass($class);
+
+        return $rc->hasMethod('_validateForWrite');
+    }
+}
diff --git a/lib/php/test/Unit/BinarySerializerTest.php b/lib/php/test/Unit/BinarySerializerTest.php
new file mode 100644
index 0000000..b97da74
--- /dev/null
+++ b/lib/php/test/Unit/BinarySerializerTest.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use PHPUnit\Framework\TestCase;
+use Thrift\ClassLoader\ThriftClassLoader;
+use Thrift\Serializer\TBinarySerializer;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r  --out ./Resources/packages/php ./Resources/ThriftTest.thrift
+ */
+class BinarySerializerTest extends TestCase
+{
+    public function setUp()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->register();
+    }
+
+    /**
+     * We try to serialize and deserialize a random object to make sure no exceptions are thrown.
+     * @see THRIFT-1579
+     */
+    public function testBinarySerializer()
+    {
+        $struct = new \ThriftTest\Xtruct(array('string_thing' => 'abc'));
+        $serialized = TBinarySerializer::serialize($struct, 'ThriftTest\\Xtruct');
+        $deserialized = TBinarySerializer::deserialize($serialized, 'ThriftTest\\Xtruct');
+        $this->assertEquals($struct, $deserialized);
+    }
+}
diff --git a/lib/php/test/Unit/JsonSerializeTest.php b/lib/php/test/Unit/JsonSerializeTest.php
new file mode 100644
index 0000000..9fdc3f3
--- /dev/null
+++ b/lib/php/test/Unit/JsonSerializeTest.php
@@ -0,0 +1,115 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use PHPUnit\Framework\TestCase;
+use stdClass;
+use Thrift\ClassLoader\ThriftClassLoader;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php:json -r  --out ./Resources/packages/phpjs ./Resources/ThriftTest.thrift
+ */
+class JsonSerializeTest extends TestCase
+{
+    protected function setUp()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/phpjs');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/phpjs');
+        $loader->register();
+    }
+
+    public function testEmptyStruct()
+    {
+        $empty = new \ThriftTest\EmptyStruct(array('non_existing_key' => 'bar'));
+        $this->assertEquals(new stdClass(), json_decode(json_encode($empty)));
+    }
+
+    public function testStringsAndInts()
+    {
+        $input = array(
+            'string_thing' => 'foo',
+            'i64_thing' => 1234567890,
+        );
+        $xtruct = new \ThriftTest\Xtruct($input);
+
+        // Xtruct's 'i32_thing' and 'byte_thing' fields should not be present here!
+        $expected = new stdClass();
+        $expected->string_thing = $input['string_thing'];
+        $expected->i64_thing = $input['i64_thing'];
+        $this->assertEquals($expected, json_decode(json_encode($xtruct)));
+    }
+
+    public function testNestedStructs()
+    {
+        $xtruct2 = new \ThriftTest\Xtruct2(array(
+            'byte_thing' => 42,
+            'struct_thing' => new \ThriftTest\Xtruct(array(
+                'i32_thing' => 123456,
+            )),
+        ));
+
+        $expected = new stdClass();
+        $expected->byte_thing = $xtruct2->byte_thing;
+        $expected->struct_thing = new stdClass();
+        $expected->struct_thing->i32_thing = $xtruct2->struct_thing->i32_thing;
+        $this->assertEquals($expected, json_decode(json_encode($xtruct2)));
+    }
+
+    public function testInsanity()
+    {
+        $xinput = array('string_thing' => 'foo');
+        $xtruct = new \ThriftTest\Xtruct($xinput);
+        $insanity = new \ThriftTest\Insanity(array(
+            'xtructs' => array($xtruct, $xtruct, $xtruct)
+        ));
+        $expected = new stdClass();
+        $expected->xtructs = array((object)$xinput, (object)$xinput, (object)$xinput);
+        $this->assertEquals($expected, json_decode(json_encode($insanity)));
+    }
+
+    public function testNestedLists()
+    {
+        $bonk = new \ThriftTest\Bonk(array('message' => 'foo'));
+        $nested = new \ThriftTest\NestedListsBonk(array('bonk' => array(array(array($bonk)))));
+        $expected = new stdClass();
+        $expected->bonk = array(array(array((object)array('message' => 'foo'))));
+        $this->assertEquals($expected, json_decode(json_encode($nested)));
+    }
+
+    public function testMaps()
+    {
+        $intmap = new \ThriftTest\ThriftTest_testMap_args(['thing' => [0 => 'zero']]);
+        $emptymap = new \ThriftTest\ThriftTest_testMap_args([]);
+        $this->assertEquals('{"thing":{"0":"zero"}}', json_encode($intmap));
+        $this->assertEquals('{}', json_encode($emptymap));
+    }
+
+    public function testScalarTypes()
+    {
+        $b = new \ThriftTest\Bools(['im_true' => '1', 'im_false' => '0']);
+        $this->assertEquals('{"im_true":true,"im_false":false}', json_encode($b));
+        $s = new \ThriftTest\StructA(['s' => 42]);
+        $this->assertEquals('{"s":"42"}', json_encode($s));
+    }
+}
diff --git a/lib/php/test/Unit/TJSONProtocolTest.php b/lib/php/test/Unit/TJSONProtocolTest.php
new file mode 100644
index 0000000..178d63b
--- /dev/null
+++ b/lib/php/test/Unit/TJSONProtocolTest.php
@@ -0,0 +1,514 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use PHPUnit\Framework\TestCase;
+use Test\Thrift\Fixtures\Fixtures;
+use Test\Thrift\Fixtures\TJSONProtocolFixtures;
+use Thrift\ClassLoader\ThriftClassLoader;
+use Thrift\Protocol\TJSONProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r  --out ./Resources/packages/php ./Resources/ThriftTest.thrift
+ */
+class TJSONProtocolTest extends TestCase
+{
+    private $transport;
+    private $protocol;
+
+    public static function setUpBeforeClass()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->register();
+
+        Fixtures::populateTestArgs();
+        TJSONProtocolFixtures::populateTestArgsJSON();
+    }
+
+    public function setUp()
+    {
+        $this->transport = new TMemoryBuffer();
+        $this->protocol = new TJSONProtocol($this->transport);
+        $this->transport->open();
+    }
+
+    /**
+     * WRITE TESTS
+     */
+    public function testVoidWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testVoid_args();
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testVoid'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString1Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testString1'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testString1'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString2Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testString2'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testString2'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testDoubleWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testDouble_args();
+        $args->thing = Fixtures::$testArgs['testDouble'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testDouble'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testByteWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testByte_args();
+        $args->thing = Fixtures::$testArgs['testByte'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testByte'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI32Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testI32_args();
+        $args->thing = Fixtures::$testArgs['testI32'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testI32'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI64Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testI64_args();
+        $args->thing = Fixtures::$testArgs['testI64'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testI64'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStructWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testStruct_args();
+        $args->thing = Fixtures::$testArgs['testStruct'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testStruct'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testNestWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testNest_args();
+        $args->thing = Fixtures::$testArgs['testNest'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testNest'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testMapWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testMap_args();
+        $args->thing = Fixtures::$testArgs['testMap'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testMap'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStringMapWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testStringMap_args();
+        $args->thing = Fixtures::$testArgs['testStringMap'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testStringMap'];
+
+        /*
+         * The $actual returns unescaped string.
+         * It is required to to decode then encode it again
+         * to get the expected escaped unicode.
+         */
+        $this->assertEquals($expected, json_encode(json_decode($actual)));
+    }
+
+    public function testSetWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testSet_args();
+        $args->thing = Fixtures::$testArgs['testSet'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testSet'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testListWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testList_args();
+        $args->thing = Fixtures::$testArgs['testList'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testList'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testEnumWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testEnum_args();
+        $args->thing = Fixtures::$testArgs['testEnum'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testEnum'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testTypedefWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testTypedef_args();
+        $args->thing = Fixtures::$testArgs['testTypedef'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testTypedef'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    /**
+     * READ TESTS
+     */
+    public function testVoidRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testVoid']
+        );
+        $args = new \ThriftTest\ThriftTest_testVoid_args();
+        $result = $args->read($this->protocol);
+
+        $this->assertEquals(0, $result);
+    }
+
+    public function testString1Read()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testString1']
+        );
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testString1'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString2Read()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testString2']
+        );
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testString2'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString3Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testString3'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testString3'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString4Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testUnicodeStringWithNonBMP'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TJSONProtocolFixtures::$testArgsJSON['testUnicodeStringWithNonBMP'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testDoubleRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testDouble']
+        );
+        $args = new \ThriftTest\ThriftTest_testDouble_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testDouble'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testByteRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testByte']
+        );
+        $args = new \ThriftTest\ThriftTest_testByte_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testByte'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI32Read()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testI32']
+        );
+        $args = new \ThriftTest\ThriftTest_testI32_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testI32'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI64Read()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testI64']
+        );
+        $args = new \ThriftTest\ThriftTest_testI64_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testI64'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStructRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testStruct']
+        );
+        $args = new \ThriftTest\ThriftTest_testStruct_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testStruct'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testNestRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testNest']
+        );
+        $args = new \ThriftTest\ThriftTest_testNest_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testNest'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testMapRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testMap']
+        );
+        $args = new \ThriftTest\ThriftTest_testMap_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testMap'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStringMapRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testStringMap']
+        );
+        $args = new \ThriftTest\ThriftTest_testStringMap_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testStringMap'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testSetRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testSet']
+        );
+        $args = new \ThriftTest\ThriftTest_testSet_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testSet'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testListRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testList']
+        );
+        $args = new \ThriftTest\ThriftTest_testList_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testList'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testEnumRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testEnum']
+        );
+        $args = new \ThriftTest\ThriftTest_testEnum_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testEnum'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testTypedefRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testTypedef']
+        );
+        $args = new \ThriftTest\ThriftTest_testTypedef_args();
+        $args->read($this->protocol);
+
+        $actual = $args->thing;
+        $expected = Fixtures::$testArgs['testTypedef'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testMapMapRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testMapMap']
+        );
+        $result = new \ThriftTest\ThriftTest_testMapMap_result();
+        $result->read($this->protocol);
+
+        $actual = $result->success;
+        $expected = Fixtures::$testArgs['testMapMapExpectedResult'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testInsanityRead()
+    {
+        $this->transport->write(
+            TJSONProtocolFixtures::$testArgsJSON['testInsanity']
+        );
+        $result = new \ThriftTest\ThriftTest_testInsanity_result();
+        $result->read($this->protocol);
+
+        $actual = $result->success;
+        $expected = Fixtures::$testArgs['testInsanityExpectedResult'];
+
+        $this->assertEquals($expected, $actual);
+    }
+}
diff --git a/lib/php/test/Unit/TSimpleJSONProtocolTest.php b/lib/php/test/Unit/TSimpleJSONProtocolTest.php
new file mode 100644
index 0000000..3189d99
--- /dev/null
+++ b/lib/php/test/Unit/TSimpleJSONProtocolTest.php
@@ -0,0 +1,247 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use PHPUnit\Framework\TestCase;
+use Test\Thrift\Fixtures\Fixtures;
+use Test\Thrift\Fixtures\TSimpleJSONProtocolFixtures;
+use Thrift\ClassLoader\ThriftClassLoader;
+use Thrift\Protocol\TSimpleJSONProtocol;
+use Thrift\Transport\TMemoryBuffer;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r  --out ./Resources/packages/php ./Resources/ThriftTest.thrift
+ */
+class TSimpleJSONProtocolTest extends TestCase
+{
+    private $transport;
+    private $protocol;
+
+    public static function setUpBeforeClass()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/php');
+        $loader->register();
+
+        Fixtures::populateTestArgs();
+        TSimpleJSONProtocolFixtures::populateTestArgsSimpleJSON();
+    }
+
+    public function setUp()
+    {
+        $this->transport = new TMemoryBuffer();
+        $this->protocol = new TSimpleJSONProtocol($this->transport);
+        $this->transport->open();
+    }
+
+    /**
+     * WRITE TESTS
+     */
+    public function testVoidWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testVoid_args();
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testVoid'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString1Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testString1'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testString1'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testString2Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testString_args();
+        $args->thing = Fixtures::$testArgs['testString2'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testString2'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testDoubleWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testDouble_args();
+        $args->thing = Fixtures::$testArgs['testDouble'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testDouble'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testByteWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testByte_args();
+        $args->thing = Fixtures::$testArgs['testByte'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testByte'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI32Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testI32_args();
+        $args->thing = Fixtures::$testArgs['testI32'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testI32'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testI64Write()
+    {
+        $args = new \ThriftTest\ThriftTest_testI64_args();
+        $args->thing = Fixtures::$testArgs['testI64'];
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testI64'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStructWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testStruct_args();
+        $args->thing = Fixtures::$testArgs['testStruct'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testStruct'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testNestWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testNest_args();
+        $args->thing = Fixtures::$testArgs['testNest'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testNest'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testMapWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testMap_args();
+        $args->thing = Fixtures::$testArgs['testMap'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testMap'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testStringMapWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testStringMap_args();
+        $args->thing = Fixtures::$testArgs['testStringMap'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testStringMap'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testSetWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testSet_args();
+        $args->thing = Fixtures::$testArgs['testSet'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testSet'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testListWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testList_args();
+        $args->thing = Fixtures::$testArgs['testList'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testList'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testEnumWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testEnum_args();
+        $args->thing = Fixtures::$testArgs['testEnum'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testEnum'];
+
+        $this->assertEquals($expected, $actual);
+    }
+
+    public function testTypedefWrite()
+    {
+        $args = new \ThriftTest\ThriftTest_testTypedef_args();
+        $args->thing = Fixtures::$testArgs['testTypedef'];
+
+        $args->write($this->protocol);
+
+        $actual = $this->transport->read(Fixtures::$bufsize);
+        $expected = TSimpleJSONProtocolFixtures::$testArgsJSON['testTypedef'];
+
+        $this->assertEquals($expected, $actual);
+    }
+}
diff --git a/lib/php/test/Unit/ValidatorTest.php b/lib/php/test/Unit/ValidatorTest.php
new file mode 100644
index 0000000..06f6e55
--- /dev/null
+++ b/lib/php/test/Unit/ValidatorTest.php
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use Thrift\ClassLoader\ThriftClassLoader;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php:validate -r  --out ./Resources/packages/phpv ./Resources/ThriftTest.thrift
+ */
+class ValidatorTest extends BaseValidatorTest
+{
+    public function setUp()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/phpv');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/phpv');
+        $loader->registerNamespace('TestValidators', __DIR__ . '/../Resources/packages/phpv');
+        $loader->registerDefinition('TestValidators', __DIR__ . '/../Resources/packages/phpv');
+        $loader->register();
+    }
+}
diff --git a/lib/php/test/Unit/ValidatorTestOop.php b/lib/php/test/Unit/ValidatorTestOop.php
new file mode 100644
index 0000000..79da11e
--- /dev/null
+++ b/lib/php/test/Unit/ValidatorTestOop.php
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit;
+
+use Thrift\ClassLoader\ThriftClassLoader;
+
+/***
+ * This test suite depends on running the compiler against the ./Resources/ThriftTest.thrift file:
+ * lib/php/test$ ../../../compiler/cpp/thrift --gen php:validate,oop -r --out ./Resources/packages/phpvo ./Resources/ThriftTest.thrift
+ */
+class ValidatorTestOop extends BaseValidatorTest
+{
+    public function setUp()
+    {
+        $loader = new ThriftClassLoader();
+        $loader->registerNamespace('ThriftTest', __DIR__ . '/../Resources/packages/phpvo');
+        $loader->registerDefinition('ThriftTest', __DIR__ . '/../Resources/packages/phpvo');
+        $loader->registerNamespace('TestValidators', __DIR__ . '/../Resources/packages/phpvo');
+        $loader->registerDefinition('TestValidators', __DIR__ . '/../Resources/packages/phpvo');
+        $loader->register();
+    }
+}