You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by ro...@apache.org on 2014/10/08 23:48:33 UTC

git commit: THRIFT-2712 PHP: add "json" option to create JsonSerializable code

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 <ro...@apache.org>
Authored: Wed Oct 8 23:28:09 2014 +0200
Committer: Roger Meier <ro...@apache.org>
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)));
+  }
+
+}