1 /*
2 *
3 * Copyright 2016 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <map>
20
21 #include <google/protobuf/compiler/php/php_generator.h>
22
23 #include "src/compiler/config.h"
24 #include "src/compiler/generator_helpers.h"
25 #include "src/compiler/php_generator_helpers.h"
26
27 using google::protobuf::compiler::php::GeneratedClassName;
28 using grpc::protobuf::Descriptor;
29 using grpc::protobuf::FileDescriptor;
30 using grpc::protobuf::MethodDescriptor;
31 using grpc::protobuf::ServiceDescriptor;
32 using grpc::protobuf::io::Printer;
33 using grpc::protobuf::io::StringOutputStream;
34 using std::map;
35
36 namespace grpc_php_generator {
37 namespace {
38
ConvertToPhpNamespace(const std::string & name)39 std::string ConvertToPhpNamespace(const std::string& name) {
40 std::vector<std::string> tokens = grpc_generator::tokenize(name, ".");
41 std::ostringstream oss;
42 for (unsigned int i = 0; i < tokens.size(); i++) {
43 oss << (i == 0 ? "" : "\\")
44 << grpc_generator::CapitalizeFirstLetter(tokens[i]);
45 }
46 return oss.str();
47 }
48
PackageName(const FileDescriptor * file)49 std::string PackageName(const FileDescriptor* file) {
50 if (file->options().has_php_namespace()) {
51 return file->options().php_namespace();
52 } else {
53 return ConvertToPhpNamespace(file->package());
54 }
55 }
56
MessageIdentifierName(const std::string & name,const FileDescriptor * file)57 std::string MessageIdentifierName(const std::string& name,
58 const FileDescriptor* file) {
59 std::vector<std::string> tokens = grpc_generator::tokenize(name, ".");
60 std::ostringstream oss;
61 if (PackageName(file) != "") {
62 oss << PackageName(file) << "\\";
63 }
64 oss << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]);
65 return oss.str();
66 }
67
PrintMethod(const MethodDescriptor * method,Printer * out)68 void PrintMethod(const MethodDescriptor* method, Printer* out) {
69 const Descriptor* input_type = method->input_type();
70 const Descriptor* output_type = method->output_type();
71 map<std::string, std::string> vars;
72 vars["service_name"] = method->service()->full_name();
73 vars["name"] = method->name();
74 vars["input_type_id"] =
75 MessageIdentifierName(GeneratedClassName(input_type), input_type->file());
76 vars["output_type_id"] = MessageIdentifierName(
77 GeneratedClassName(output_type), output_type->file());
78
79 out->Print("/**\n");
80 if (method->options().deprecated()) {
81 out->Print(" * @deprecated\n");
82 }
83 out->Print(GetPHPComments(method, " *").c_str());
84 if (method->client_streaming()) {
85 if (method->server_streaming()) {
86 vars["return_type_id"] = "\\Grpc\\BidiStreamingCall";
87 } else {
88 vars["return_type_id"] = "\\Grpc\\ClientStreamingCall";
89 }
90 out->Print(vars,
91 " * @param array $$metadata metadata\n"
92 " * @param array $$options call options\n"
93 " * @return $return_type_id$\n */\n"
94 "public function $name$($$metadata = [], "
95 "$$options = []) {\n");
96 out->Indent();
97 out->Indent();
98 if (method->server_streaming()) {
99 out->Print("return $$this->_bidiRequest(");
100 } else {
101 out->Print("return $$this->_clientStreamRequest(");
102 }
103 out->Print(vars,
104 "'/$service_name$/$name$',\n"
105 "['\\$output_type_id$','decode'],\n"
106 "$$metadata, $$options);\n");
107 } else {
108 if (method->server_streaming()) {
109 vars["return_type_id"] = "\\Grpc\\ServerStreamingCall";
110 } else {
111 vars["return_type_id"] = "\\Grpc\\UnaryCall";
112 }
113 out->Print(vars,
114 " * @param \\$input_type_id$ $$argument input argument\n"
115 " * @param array $$metadata metadata\n"
116 " * @param array $$options call options\n"
117 " * @return $return_type_id$\n */\n"
118 "public function $name$(\\$input_type_id$ $$argument,\n"
119 " $$metadata = [], $$options = []) {\n");
120 out->Indent();
121 out->Indent();
122 if (method->server_streaming()) {
123 out->Print("return $$this->_serverStreamRequest(");
124 } else {
125 out->Print("return $$this->_simpleRequest(");
126 }
127 out->Print(vars,
128 "'/$service_name$/$name$',\n"
129 "$$argument,\n"
130 "['\\$output_type_id$', 'decode'],\n"
131 "$$metadata, $$options);\n");
132 }
133 out->Outdent();
134 out->Outdent();
135 out->Print("}\n\n");
136 }
137
PrintServerMethod(const MethodDescriptor * method,Printer * out)138 void PrintServerMethod(const MethodDescriptor* method, Printer* out) {
139 map<std::string, std::string> vars;
140 const Descriptor* input_type = method->input_type();
141 const Descriptor* output_type = method->output_type();
142 vars["service_name"] = method->service()->full_name();
143 vars["method_name"] = method->name();
144 vars["input_type_id"] =
145 MessageIdentifierName(GeneratedClassName(input_type), input_type->file());
146 vars["output_type_id"] = MessageIdentifierName(
147 GeneratedClassName(output_type), output_type->file());
148
149 out->Print("/**\n");
150 if (method->options().deprecated()) {
151 out->Print(" * @deprecated\n");
152 }
153 out->Print(GetPHPComments(method, " *").c_str());
154
155 const char* method_template;
156 if (method->client_streaming() && method->server_streaming()) {
157 method_template =
158 " * @param \\Grpc\\ServerCallReader $$reader read client request data "
159 "of \\$input_type_id$\n"
160 " * @param \\Grpc\\ServerCallWriter $$writer write response data of "
161 "\\$output_type_id$\n"
162 " * @param \\Grpc\\ServerContext $$context server request context\n"
163 " * @return void\n"
164 " */\n"
165 "public function $method_name$(\n"
166 " \\Grpc\\ServerCallReader $$reader,\n"
167 " \\Grpc\\ServerCallWriter $$writer,\n"
168 " \\Grpc\\ServerContext $$context\n"
169 "): void {\n"
170 " $$context->setStatus(\\Grpc\\Status::unimplemented());\n"
171 " $$writer->finish();\n"
172 "}\n\n";
173 } else if (method->client_streaming()) {
174 method_template =
175 " * @param \\Grpc\\ServerCallReader $$reader read client request data "
176 "of \\$input_type_id$\n"
177 " * @param \\Grpc\\ServerContext $$context server request context\n"
178 " * @return \\$output_type_id$ for response data, null if if error "
179 "occured\n"
180 " * initial metadata (if any) and status (if not ok) should be set "
181 "to $$context\n"
182 " */\n"
183 "public function $method_name$(\n"
184 " \\Grpc\\ServerCallReader $$reader,\n"
185 " \\Grpc\\ServerContext $$context\n"
186 "): ?\\$output_type_id$ {\n"
187 " $$context->setStatus(\\Grpc\\Status::unimplemented());\n"
188 " return null;\n"
189 "}\n\n";
190 } else if (method->server_streaming()) {
191 method_template =
192 " * @param \\$input_type_id$ $$request client request\n"
193 " * @param \\Grpc\\ServerCallWriter $$writer write response data of "
194 "\\$output_type_id$\n"
195 " * @param \\Grpc\\ServerContext $$context server request context\n"
196 " * @return void\n"
197 " */\n"
198 "public function $method_name$(\n"
199 " \\$input_type_id$ $$request,\n"
200 " \\Grpc\\ServerCallWriter $$writer,\n"
201 " \\Grpc\\ServerContext $$context\n"
202 "): void {\n"
203 " $$context->setStatus(\\Grpc\\Status::unimplemented());\n"
204 " $$writer->finish();\n"
205 "}\n\n";
206 } else {
207 method_template =
208 " * @param \\$input_type_id$ $$request client request\n"
209 " * @param \\Grpc\\ServerContext $$context server request context\n"
210 " * @return \\$output_type_id$ for response data, null if if error "
211 "occured\n"
212 " * initial metadata (if any) and status (if not ok) should be set "
213 "to $$context\n"
214 " */\n"
215 "public function $method_name$(\n"
216 " \\$input_type_id$ $$request,\n"
217 " \\Grpc\\ServerContext $$context\n"
218 "): ?\\$output_type_id$ {\n"
219 " $$context->setStatus(\\Grpc\\Status::unimplemented());\n"
220 " return null;\n"
221 "}\n\n";
222 }
223 out->Print(vars, method_template);
224 }
225
PrintServerMethodDescriptors(const ServiceDescriptor * service,Printer * out)226 void PrintServerMethodDescriptors(const ServiceDescriptor* service,
227 Printer* out) {
228 map<std::string, std::string> vars;
229 vars["service_name"] = service->full_name();
230
231 out->Print("/**\n");
232 if (service->options().deprecated()) {
233 out->Print(" * @deprecated\n");
234 }
235 out->Print(
236 " * Get the method descriptors of the service for server registration\n"
237 " *\n"
238 " * @return array of \\Grpc\\MethodDescriptor for the service methods\n"
239 " */\n"
240 "public final function getMethodDescriptors(): array\n{\n");
241 out->Indent();
242 out->Indent();
243 out->Print("return [\n");
244 out->Indent();
245 out->Indent();
246 for (int i = 0; i < service->method_count(); i++) {
247 auto method = service->method(i);
248 auto input_type = method->input_type();
249 vars["method_name"] = method->name();
250 vars["input_type_id"] = MessageIdentifierName(
251 GeneratedClassName(input_type), input_type->file());
252 if (method->client_streaming() && method->server_streaming()) {
253 vars["call_type"] = "BIDI_STREAMING_CALL";
254 } else if (method->client_streaming()) {
255 vars["call_type"] = "CLIENT_STREAMING_CALL";
256 } else if (method->server_streaming()) {
257 vars["call_type"] = "SERVER_STREAMING_CALL";
258 } else {
259 vars["call_type"] = "UNARY_CALL";
260 }
261 out->Print(
262 vars,
263 "'/$service_name$/$method_name$' => new \\Grpc\\MethodDescriptor(\n"
264 " $$this,\n"
265 " '$method_name$',\n"
266 " '\\$input_type_id$',\n"
267 " \\Grpc\\MethodDescriptor::$call_type$\n"
268 "),\n");
269 }
270 out->Outdent();
271 out->Outdent();
272 out->Print("];\n");
273 out->Outdent();
274 out->Outdent();
275 out->Print("}\n\n");
276 }
277
278 // Prints out the service descriptor object
PrintService(const ServiceDescriptor * service,const std::string & class_suffix,bool is_server,Printer * out)279 void PrintService(const ServiceDescriptor* service,
280 const std::string& class_suffix, bool is_server,
281 Printer* out) {
282 map<std::string, std::string> vars;
283 out->Print("/**\n");
284 if (service->options().deprecated()) {
285 out->Print(" * @deprecated\n");
286 }
287 out->Print(GetPHPComments(service, " *").c_str());
288 out->Print(" */\n");
289 vars["name"] = GetPHPServiceClassname(service, class_suffix, is_server);
290 vars["extends"] = is_server ? "" : "extends \\Grpc\\BaseStub ";
291 out->Print(vars, "class $name$ $extends${\n\n");
292 out->Indent();
293 out->Indent();
294 if (!is_server) {
295 out->Print(
296 "/**\n * @param string $$hostname hostname\n"
297 " * @param array $$opts channel options\n"
298 " * @param \\Grpc\\Channel $$channel (optional) re-use channel object\n"
299 " */\n"
300 "public function __construct($$hostname, $$opts, "
301 "$$channel = null) {\n");
302 out->Indent();
303 out->Indent();
304 out->Print("parent::__construct($$hostname, $$opts, $$channel);\n");
305 out->Outdent();
306 out->Outdent();
307 out->Print("}\n\n");
308 }
309 for (int i = 0; i < service->method_count(); i++) {
310 if (is_server) {
311 PrintServerMethod(service->method(i), out);
312 } else {
313 PrintMethod(service->method(i), out);
314 }
315 }
316 if (is_server) {
317 PrintServerMethodDescriptors(service, out);
318 }
319 out->Outdent();
320 out->Outdent();
321 out->Print("}\n");
322 }
323 } // namespace
324
GenerateFile(const FileDescriptor * file,const ServiceDescriptor * service,const std::string & class_suffix,bool is_server)325 std::string GenerateFile(const FileDescriptor* file,
326 const ServiceDescriptor* service,
327 const std::string& class_suffix, bool is_server) {
328 std::string output;
329 {
330 StringOutputStream output_stream(&output);
331 Printer out(&output_stream, '$');
332
333 out.Print("<?php\n");
334 out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
335
336 std::string leading_comments = GetPHPComments(file, "//");
337 if (!leading_comments.empty()) {
338 out.Print("// Original file comments:\n");
339 out.PrintRaw(leading_comments.c_str());
340 }
341
342 map<std::string, std::string> vars;
343 std::string php_namespace = PackageName(file);
344 vars["package"] = php_namespace;
345 out.Print(vars, "namespace $package$;\n\n");
346
347 PrintService(service, class_suffix, is_server, &out);
348 }
349 return output;
350 }
351
352 } // namespace grpc_php_generator
353