Repository: thrift Updated Branches: refs/heads/master d5f3be5eb -> 964082a8e
THRIFT-2712 PHP: add "json" option to create JsonSerializable code This patch adds a "json" option to PHP code generation that will make generated classes implement JsonSerializable, so thrift objects may be converted to json using json_encode() easily. If the "validate" option is enabled, the object's write validator will be called, beyond that the jsonSerialize() method only outputs non-null fields in the JSON object (JSON parsers get grumpy if you send them null where they expect to see a number). Patch: Stig Bakken Github Pull Request: This closes #219 Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/964082a8 Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/964082a8 Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/964082a8 Branch: refs/heads/master Commit: 964082a8eb06a5f36eff87132a048cbb3ea5e7a5 Parents: d5f3be5 Author: Roger Meier <[email protected]> Authored: Wed Oct 8 23:28:09 2014 +0200 Committer: Roger Meier <[email protected]> Committed: Wed Oct 8 23:41:18 2014 +0200 ---------------------------------------------------------------------- .gitignore | 3 +- compiler/cpp/src/generate/t_php_generator.cc | 84 +++++++++++++++--- lib/php/test/Makefile.am | 23 +++-- .../Thrift/JsonSerialize/JsonSerializeTest.php | 92 ++++++++++++++++++++ 4 files changed, 184 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/964082a8/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index f072ade..de55b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -168,6 +168,7 @@ test-driver /lib/js/test/build /lib/nodejs/node_modules/ /lib/perl/MANIFEST +/lib/perl/MYMETA.json /lib/perl/MYMETA.yml /lib/perl/Makefile-perl.mk /lib/perl/blib @@ -199,7 +200,7 @@ test-driver /lib/php/src/ext/thrift_protocol/thrift_protocol.la /lib/php/src/ext/thrift_protocol/tmp-php.ini /lib/php/src/packages/ -/lib/php/test/phpunit.xml +/lib/php/test/TEST-*.xml /lib/php/test/packages/ /lib/py/dist/ /lib/erl/logs/ http://git-wip-us.apache.org/repos/asf/thrift/blob/964082a8/compiler/cpp/src/generate/t_php_generator.cc ---------------------------------------------------------------------- diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc index a787f70..0c34a13 100644 --- a/compiler/cpp/src/generate/t_php_generator.cc +++ b/compiler/cpp/src/generate/t_php_generator.cc @@ -73,6 +73,9 @@ class t_php_generator : public t_oop_generator { iter = parsed_options.find("validate"); validate_ = (iter != parsed_options.end()); + iter = parsed_options.find("json"); + json_serializable_ = (iter != parsed_options.end()); + iter = parsed_options.find("nsglobal"); if(iter != parsed_options.end()) { nsglobal_ = iter->second; @@ -124,6 +127,7 @@ class t_php_generator : public t_oop_generator { void generate_php_struct_required_validator(ofstream& out, t_struct* tstruct, std::string method_name, bool write_mode); void generate_php_struct_read_validator(ofstream& out, t_struct* tstruct); void generate_php_struct_write_validator(ofstream& out, t_struct* tstruct); + void generate_php_struct_json_serialize(ofstream& out, t_struct* tstruct, bool is_result); bool needs_php_write_validator(t_struct* tstruct, bool is_result); bool needs_php_read_validator(t_struct* tstruct, bool is_result); int get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode); @@ -378,6 +382,11 @@ class t_php_generator : public t_oop_generator { bool validate_; /** + * Whether to generate JsonSerializable classes + */ + bool json_serializable_; + + /** * Global namespace for PHP 5.3 */ std::string nsglobal_; @@ -425,16 +434,23 @@ void t_php_generator::init_generator() { * Prints standard php includes */ string t_php_generator::php_includes() { - string TBase = "use Thrift\\Base\\TBase;\n"; - string TType = "use Thrift\\Type\\TType;\n"; - string TMessageType = "use Thrift\\Type\\TMessageType;\n"; - string TException = "use Thrift\\Exception\\TException;\n"; - string TProtocolException = "use Thrift\\Exception\\TProtocolException;\n"; - string TProtocol = "use Thrift\\Protocol\\TProtocol;\n"; - string TBinaryProtocolAccelerated = "use Thrift\\Protocol\\TBinaryProtocolAccelerated;\n"; - string TApplicationException = "use Thrift\\Exception\\TApplicationException;\n\n"; - - return TBase + TType + TMessageType + TException + TProtocolException + TProtocol + TBinaryProtocolAccelerated + TApplicationException; + string includes = + "use Thrift\\Base\\TBase;\n" + "use Thrift\\Type\\TType;\n" + "use Thrift\\Type\\TMessageType;\n" + "use Thrift\\Exception\\TException;\n" + "use Thrift\\Exception\\TProtocolException;\n" + "use Thrift\\Protocol\\TProtocol;\n" + "use Thrift\\Protocol\\TBinaryProtocolAccelerated;\n" + "use Thrift\\Exception\\TApplicationException;\n"; + + if (json_serializable_) { + includes += + "use JsonSerializable;\n" + "use stdClass;\n"; + } + + return includes + "\n"; } /** @@ -770,6 +786,9 @@ void t_php_generator::generate_php_struct_definition(ofstream& out, } else if (oop_) { out << " extends " << "TBase"; } + if (json_serializable_) { + out << " implements JsonSerializable"; + } out << " {" << endl; indent_up(); @@ -838,6 +857,9 @@ void t_php_generator::generate_php_struct_definition(ofstream& out, if (needs_php_write_validator(tstruct, is_result)) { generate_php_struct_write_validator(out, tstruct); } + if (json_serializable_) { + generate_php_struct_json_serialize(out, tstruct, is_result); + } indent_down(); out << @@ -1125,6 +1147,47 @@ void t_php_generator::generate_php_struct_required_validator(ofstream& out, indent(out) << "}" << endl << endl; } + + +void t_php_generator::generate_php_struct_json_serialize(ofstream& out, + t_struct* tstruct, + bool is_result) { + indent(out) << + "public function jsonSerialize() {" << endl; + indent_up(); + + if (needs_php_write_validator(tstruct, is_result)) { + indent(out) << "$this->_validateForWrite();" << endl; + } + + indent(out) << "$json = new stdClass;" << endl; + + const vector<t_field*>& fields = tstruct->get_members(); + + if (fields.size() > 0) { + vector<t_field*>::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + t_type* type = field->get_type(); + const string& name = field->get_name(); + if (type->is_map() && !((t_map*)type)->get_key_type()->is_string()) { + // JSON object keys must be strings + continue; + } + indent(out) << "if ($this->" << name << " !== null) {" << endl; + indent_up(); + indent(out) << "$json->" << name << " = $this->" << name << ";" << endl; + indent_down(); + indent(out) << "}" << endl; + } + } + + indent(out) << "return $json;" << endl; + indent_down(); + + indent(out) << "}" << endl << endl; +} + int t_php_generator::get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode) { int num_req = 0; @@ -2695,5 +2758,6 @@ THRIFT_REGISTER_GENERATOR(php, "PHP", " rest: Generate PHP REST processors\n" " nsglobal=NAME: Set global namespace\n" " validate: Generate PHP validator methods\n" +" json: Generate JsonSerializable classes (requires PHP >= 5.4)\n" ) http://git-wip-us.apache.org/repos/asf/thrift/blob/964082a8/lib/php/test/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/php/test/Makefile.am b/lib/php/test/Makefile.am index a529d8c..869c544 100755 --- a/lib/php/test/Makefile.am +++ b/lib/php/test/Makefile.am @@ -24,25 +24,34 @@ stubs: ../../../test/ThriftTest.thrift TestValidators.thrift $(THRIFT) --gen php -r --out ./packages ../../../test/ThriftTest.thrift mkdir -p ./packages/phpv mkdir -p ./packages/phpvo - $(THRIFT) --gen php:validate -r --out ./packages/phpv TestValidators.thrift - $(THRIFT) --gen php:validate,oop -r --out ./packages/phpvo TestValidators.thrift - + mkdir -p ./packages/phpjs + $(THRIFT) --gen php:validate -r --out ./packages/phpv TestValidators.thrift + $(THRIFT) --gen php:validate,oop -r --out ./packages/phpvo TestValidators.thrift + $(THRIFT) --gen php:json -r --out ./packages/phpjs TestValidators.thrift + +check-json-serializer: stubs +if HAVE_PHPUNIT + $(PHPUNIT) --log-junit=TEST-json-serializer.xml Test/Thrift/JsonSerialize/ +endif + check-validator: stubs php Test/Thrift/TestValidators.php php Test/Thrift/TestValidators.php -oop check-protocol: stubs if HAVE_PHPUNIT - $(PHPUNIT) --log-junit=phpunit.xml Test/Thrift/Protocol/TestTJSONProtocol.php - $(PHPUNIT) --log-junit=phpunit.xml Test/Thrift/Protocol/TestBinarySerializer.php + $(PHPUNIT) --log-junit=TEST-log-json-protocol.xml Test/Thrift/Protocol/TestTJSONProtocol.php + $(PHPUNIT) --log-junit=TEST-binary-serializer.xml Test/Thrift/Protocol/TestBinarySerializer.php endif - + check: stubs \ check-protocol \ - check-validator + check-validator \ + check-json-serializer clean-local: $(RM) -r ./packages + $(RM) TEST-*.xml EXTRA_DIST = Test http://git-wip-us.apache.org/repos/asf/thrift/blob/964082a8/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php ---------------------------------------------------------------------- diff --git a/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php b/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php new file mode 100644 index 0000000..7e324f0 --- /dev/null +++ b/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php @@ -0,0 +1,92 @@ +<?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\JsonSerialize; + +use stdClass; +use Test\Thrift\Fixtures; +use Thrift\ClassLoader\ThriftClassLoader; +use Thrift\Serializer\TBinarySerializer; + +require_once __DIR__.'/../../../../lib/Thrift/ClassLoader/ThriftClassLoader.php'; + +$loader = new ThriftClassLoader(); +$loader->registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages/phpjs'); +$loader->register(); + +class JsonSerializeTest extends \PHPUnit_Framework_TestCase +{ + + 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))); + } + +}
