php_generator.cc 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #include <google/protobuf/compiler/php/php_generator.h>
  31. #include <google/protobuf/compiler/code_generator.h>
  32. #include <google/protobuf/compiler/plugin.h>
  33. #include <google/protobuf/descriptor.h>
  34. #include <google/protobuf/descriptor.pb.h>
  35. #include <google/protobuf/io/printer.h>
  36. #include <google/protobuf/io/zero_copy_stream.h>
  37. #include <google/protobuf/stubs/strutil.h>
  38. #include <sstream>
  39. const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
  40. const std::string kEmptyFile = "google/protobuf/empty.proto";
  41. const std::string kEmptyMetadataFile = "GPBMetadata/Google/Protobuf/GPBEmpty.php";
  42. const std::string kDescriptorMetadataFile =
  43. "GPBMetadata/Google/Protobuf/Internal/Descriptor.php";
  44. const std::string kDescriptorDirName = "Google/Protobuf/Internal";
  45. const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
  46. const char* const kReservedNames[] = {
  47. "abstract", "and", "array", "as", "break",
  48. "callable", "case", "catch", "class", "clone",
  49. "const", "continue", "declare", "default", "die",
  50. "do", "echo", "else", "elseif", "empty",
  51. "enddeclare", "endfor", "endforeach", "endif", "endswitch",
  52. "endwhile", "eval", "exit", "extends", "final",
  53. "for", "foreach", "function", "global", "goto",
  54. "if", "implements", "include", "include_once", "instanceof",
  55. "insteadof", "interface", "isset", "list", "namespace",
  56. "new", "or", "print", "private", "protected",
  57. "public", "require", "require_once", "return", "static",
  58. "switch", "throw", "trait", "try", "unset",
  59. "use", "var", "while", "xor", "int",
  60. "float", "bool", "string", "true", "false",
  61. "null", "void", "iterable"};
  62. const char* const kValidConstantNames[] = {
  63. "int", "float", "bool", "string", "true",
  64. "false", "null", "void", "iterable",
  65. };
  66. const int kReservedNamesSize = 73;
  67. const int kValidConstantNamesSize = 9;
  68. const int kFieldSetter = 1;
  69. const int kFieldGetter = 2;
  70. const int kFieldProperty = 3;
  71. namespace google {
  72. namespace protobuf {
  73. namespace compiler {
  74. namespace php {
  75. // Forward decls.
  76. std::string PhpName(const std::string& full_name, bool is_descriptor);
  77. std::string DefaultForField(FieldDescriptor* field);
  78. std::string IntToString(int32 value);
  79. std::string FilenameToClassname(const string& filename);
  80. std::string GeneratedMetadataFileName(const FileDescriptor* file,
  81. bool is_descriptor);
  82. std::string LabelForField(FieldDescriptor* field);
  83. std::string TypeName(FieldDescriptor* field);
  84. std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
  85. std::string EscapeDollor(const string& to_escape);
  86. std::string BinaryToHex(const string& binary);
  87. void Indent(io::Printer* printer);
  88. void Outdent(io::Printer* printer);
  89. void GenerateMessageDocComment(io::Printer* printer, const Descriptor* message,
  90. int is_descriptor);
  91. void GenerateMessageConstructorDocComment(io::Printer* printer,
  92. const Descriptor* message,
  93. int is_descriptor);
  94. void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
  95. int is_descriptor, int function_type);
  96. void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
  97. int is_descriptor);
  98. void GenerateEnumValueDocComment(io::Printer* printer,
  99. const EnumValueDescriptor* value);
  100. void GenerateServiceDocComment(io::Printer* printer,
  101. const ServiceDescriptor* service);
  102. void GenerateServiceMethodDocComment(io::Printer* printer,
  103. const MethodDescriptor* method);
  104. std::string ReservedNamePrefix(const string& classname,
  105. const FileDescriptor* file) {
  106. bool is_reserved = false;
  107. string lower = classname;
  108. transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
  109. for (int i = 0; i < kReservedNamesSize; i++) {
  110. if (lower == kReservedNames[i]) {
  111. is_reserved = true;
  112. break;
  113. }
  114. }
  115. if (is_reserved) {
  116. if (file->package() == "google.protobuf") {
  117. return "GPB";
  118. } else {
  119. return "PB";
  120. }
  121. }
  122. return "";
  123. }
  124. template <typename DescriptorType>
  125. std::string DescriptorFullName(const DescriptorType* desc, bool is_descriptor) {
  126. if (is_descriptor) {
  127. return StringReplace(desc->full_name(),
  128. "google.protobuf",
  129. "google.protobuf.internal", false);
  130. } else {
  131. return desc->full_name();
  132. }
  133. }
  134. template <typename DescriptorType>
  135. std::string ClassNamePrefix(const string& classname,
  136. const DescriptorType* desc) {
  137. const string& prefix = (desc->file()->options()).php_class_prefix();
  138. if (prefix != "") {
  139. return prefix;
  140. }
  141. return ReservedNamePrefix(classname, desc->file());
  142. }
  143. template <typename DescriptorType>
  144. std::string GeneratedClassNameImpl(const DescriptorType* desc) {
  145. std::string classname = ClassNamePrefix(desc->name(), desc) + desc->name();
  146. const Descriptor* containing = desc->containing_type();
  147. while (containing != NULL) {
  148. classname = ClassNamePrefix(containing->name(), desc) + containing->name()
  149. + '\\' + classname;
  150. containing = containing->containing_type();
  151. }
  152. return classname;
  153. }
  154. std::string GeneratedClassNameImpl(const ServiceDescriptor* desc) {
  155. std::string classname = desc->name();
  156. return ClassNamePrefix(classname, desc) + classname;
  157. }
  158. std::string GeneratedClassName(const Descriptor* desc) {
  159. return GeneratedClassNameImpl(desc);
  160. }
  161. std::string GeneratedClassName(const EnumDescriptor* desc) {
  162. return GeneratedClassNameImpl(desc);
  163. }
  164. std::string GeneratedClassName(const ServiceDescriptor* desc) {
  165. return GeneratedClassNameImpl(desc);
  166. }
  167. template <typename DescriptorType>
  168. std::string LegacyGeneratedClassName(const DescriptorType* desc) {
  169. std::string classname = desc->name();
  170. const Descriptor* containing = desc->containing_type();
  171. while (containing != NULL) {
  172. classname = containing->name() + '_' + classname;
  173. containing = containing->containing_type();
  174. }
  175. return ClassNamePrefix(classname, desc) + classname;
  176. }
  177. std::string ClassNamePrefix(const string& classname) {
  178. string lower = classname;
  179. transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
  180. for (int i = 0; i < kReservedNamesSize; i++) {
  181. if (lower == kReservedNames[i]) {
  182. return "PB";
  183. }
  184. }
  185. return "";
  186. }
  187. std::string ConstantNamePrefix(const string& classname) {
  188. bool is_reserved = false;
  189. string lower = classname;
  190. transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
  191. for (int i = 0; i < kReservedNamesSize; i++) {
  192. if (lower == kReservedNames[i]) {
  193. is_reserved = true;
  194. break;
  195. }
  196. }
  197. for (int i = 0; i < kValidConstantNamesSize; i++) {
  198. if (lower == kValidConstantNames[i]) {
  199. is_reserved = false;
  200. break;
  201. }
  202. }
  203. if (is_reserved) {
  204. return "PB";
  205. }
  206. return "";
  207. }
  208. template <typename DescriptorType>
  209. std::string RootPhpNamespace(const DescriptorType* desc, bool is_descriptor) {
  210. if (desc->file()->options().has_php_namespace()) {
  211. const string& php_namespace = desc->file()->options().php_namespace();
  212. if (php_namespace != "") {
  213. return php_namespace;
  214. }
  215. return "";
  216. }
  217. if (desc->file()->package() != "") {
  218. return PhpName(desc->file()->package(), is_descriptor);
  219. }
  220. return "";
  221. }
  222. template <typename DescriptorType>
  223. std::string FullClassName(const DescriptorType* desc, bool is_descriptor) {
  224. string classname = GeneratedClassNameImpl(desc);
  225. string php_namespace = RootPhpNamespace(desc, is_descriptor);
  226. if (php_namespace != "") {
  227. return php_namespace + "\\" + classname;
  228. }
  229. return classname;
  230. }
  231. template <typename DescriptorType>
  232. std::string LegacyFullClassName(const DescriptorType* desc, bool is_descriptor) {
  233. string classname = LegacyGeneratedClassName(desc);
  234. string php_namespace = RootPhpNamespace(desc, is_descriptor);
  235. if (php_namespace != "") {
  236. return php_namespace + "\\" + classname;
  237. }
  238. return classname;
  239. }
  240. std::string PhpName(const std::string& full_name, bool is_descriptor) {
  241. if (is_descriptor) {
  242. return kDescriptorPackageName;
  243. }
  244. std::string segment;
  245. std::string result;
  246. bool cap_next_letter = true;
  247. for (int i = 0; i < full_name.size(); i++) {
  248. if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
  249. segment += full_name[i] + ('A' - 'a');
  250. cap_next_letter = false;
  251. } else if (full_name[i] == '.') {
  252. result += ClassNamePrefix(segment) + segment + '\\';
  253. segment = "";
  254. cap_next_letter = true;
  255. } else {
  256. segment += full_name[i];
  257. cap_next_letter = false;
  258. }
  259. }
  260. result += ClassNamePrefix(segment) + segment;
  261. return result;
  262. }
  263. std::string DefaultForField(const FieldDescriptor* field) {
  264. switch (field->type()) {
  265. case FieldDescriptor::TYPE_INT32:
  266. case FieldDescriptor::TYPE_INT64:
  267. case FieldDescriptor::TYPE_UINT32:
  268. case FieldDescriptor::TYPE_UINT64:
  269. case FieldDescriptor::TYPE_SINT32:
  270. case FieldDescriptor::TYPE_SINT64:
  271. case FieldDescriptor::TYPE_FIXED32:
  272. case FieldDescriptor::TYPE_FIXED64:
  273. case FieldDescriptor::TYPE_SFIXED32:
  274. case FieldDescriptor::TYPE_SFIXED64:
  275. case FieldDescriptor::TYPE_ENUM: return "0";
  276. case FieldDescriptor::TYPE_DOUBLE:
  277. case FieldDescriptor::TYPE_FLOAT: return "0.0";
  278. case FieldDescriptor::TYPE_BOOL: return "false";
  279. case FieldDescriptor::TYPE_STRING:
  280. case FieldDescriptor::TYPE_BYTES: return "''";
  281. case FieldDescriptor::TYPE_MESSAGE:
  282. case FieldDescriptor::TYPE_GROUP: return "null";
  283. default: assert(false); return "";
  284. }
  285. }
  286. std::string GeneratedMetadataFileName(const FileDescriptor* file,
  287. bool is_descriptor) {
  288. const string& proto_file = file->name();
  289. int start_index = 0;
  290. int first_index = proto_file.find_first_of("/", start_index);
  291. std::string result = "";
  292. std::string segment = "";
  293. if (proto_file == kEmptyFile) {
  294. return kEmptyMetadataFile;
  295. }
  296. if (is_descriptor) {
  297. return kDescriptorMetadataFile;
  298. }
  299. // Append directory name.
  300. std::string file_no_suffix;
  301. int lastindex = proto_file.find_last_of(".");
  302. if (proto_file == kEmptyFile) {
  303. return kEmptyMetadataFile;
  304. } else {
  305. file_no_suffix = proto_file.substr(0, lastindex);
  306. }
  307. if (file->options().has_php_metadata_namespace()) {
  308. const string& php_metadata_namespace =
  309. file->options().php_metadata_namespace();
  310. if (php_metadata_namespace != "" && php_metadata_namespace != "\\") {
  311. result += php_metadata_namespace;
  312. std::replace(result.begin(), result.end(), '\\', '/');
  313. if (result.at(result.size() - 1) != '/') {
  314. result += "/";
  315. }
  316. }
  317. } else {
  318. result += "GPBMetadata/";
  319. while (first_index != string::npos) {
  320. segment = UnderscoresToCamelCase(
  321. file_no_suffix.substr(start_index, first_index - start_index), true);
  322. result += ReservedNamePrefix(segment, file) + segment + "/";
  323. start_index = first_index + 1;
  324. first_index = file_no_suffix.find_first_of("/", start_index);
  325. }
  326. }
  327. // Append file name.
  328. int file_name_start = file_no_suffix.find_last_of("/");
  329. if (file_name_start == string::npos) {
  330. file_name_start = 0;
  331. } else {
  332. file_name_start += 1;
  333. }
  334. segment = UnderscoresToCamelCase(
  335. file_no_suffix.substr(file_name_start, first_index - file_name_start), true);
  336. return result + ReservedNamePrefix(segment, file) + segment + ".php";
  337. }
  338. template <typename DescriptorType>
  339. std::string GeneratedClassFileName(const DescriptorType* desc,
  340. bool is_descriptor) {
  341. std::string result = FullClassName(desc, is_descriptor);
  342. for (int i = 0; i < result.size(); i++) {
  343. if (result[i] == '\\') {
  344. result[i] = '/';
  345. }
  346. }
  347. return result + ".php";
  348. }
  349. template <typename DescriptorType>
  350. std::string LegacyGeneratedClassFileName(const DescriptorType* desc,
  351. bool is_descriptor) {
  352. std::string result = LegacyFullClassName(desc, is_descriptor);
  353. for (int i = 0; i < result.size(); i++) {
  354. if (result[i] == '\\') {
  355. result[i] = '/';
  356. }
  357. }
  358. return result + ".php";
  359. }
  360. std::string GeneratedServiceFileName(const ServiceDescriptor* service,
  361. bool is_descriptor) {
  362. std::string result = FullClassName(service, is_descriptor) + "Interface";
  363. for (int i = 0; i < result.size(); i++) {
  364. if (result[i] == '\\') {
  365. result[i] = '/';
  366. }
  367. }
  368. return result + ".php";
  369. }
  370. std::string IntToString(int32 value) {
  371. std::ostringstream os;
  372. os << value;
  373. return os.str();
  374. }
  375. std::string LabelForField(const FieldDescriptor* field) {
  376. switch (field->label()) {
  377. case FieldDescriptor::LABEL_OPTIONAL: return "optional";
  378. case FieldDescriptor::LABEL_REQUIRED: return "required";
  379. case FieldDescriptor::LABEL_REPEATED: return "repeated";
  380. default: assert(false); return "";
  381. }
  382. }
  383. std::string TypeName(const FieldDescriptor* field) {
  384. switch (field->type()) {
  385. case FieldDescriptor::TYPE_INT32: return "int32";
  386. case FieldDescriptor::TYPE_INT64: return "int64";
  387. case FieldDescriptor::TYPE_UINT32: return "uint32";
  388. case FieldDescriptor::TYPE_UINT64: return "uint64";
  389. case FieldDescriptor::TYPE_SINT32: return "sint32";
  390. case FieldDescriptor::TYPE_SINT64: return "sint64";
  391. case FieldDescriptor::TYPE_FIXED32: return "fixed32";
  392. case FieldDescriptor::TYPE_FIXED64: return "fixed64";
  393. case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
  394. case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
  395. case FieldDescriptor::TYPE_DOUBLE: return "double";
  396. case FieldDescriptor::TYPE_FLOAT: return "float";
  397. case FieldDescriptor::TYPE_BOOL: return "bool";
  398. case FieldDescriptor::TYPE_ENUM: return "enum";
  399. case FieldDescriptor::TYPE_STRING: return "string";
  400. case FieldDescriptor::TYPE_BYTES: return "bytes";
  401. case FieldDescriptor::TYPE_MESSAGE: return "message";
  402. case FieldDescriptor::TYPE_GROUP: return "group";
  403. default: assert(false); return "";
  404. }
  405. }
  406. std::string PhpSetterTypeName(const FieldDescriptor* field, bool is_descriptor) {
  407. if (field->is_map()) {
  408. return "array|\\Google\\Protobuf\\Internal\\MapField";
  409. }
  410. string type;
  411. switch (field->type()) {
  412. case FieldDescriptor::TYPE_INT32:
  413. case FieldDescriptor::TYPE_UINT32:
  414. case FieldDescriptor::TYPE_SINT32:
  415. case FieldDescriptor::TYPE_FIXED32:
  416. case FieldDescriptor::TYPE_SFIXED32:
  417. case FieldDescriptor::TYPE_ENUM:
  418. type = "int";
  419. break;
  420. case FieldDescriptor::TYPE_INT64:
  421. case FieldDescriptor::TYPE_UINT64:
  422. case FieldDescriptor::TYPE_SINT64:
  423. case FieldDescriptor::TYPE_FIXED64:
  424. case FieldDescriptor::TYPE_SFIXED64:
  425. type = "int|string";
  426. break;
  427. case FieldDescriptor::TYPE_DOUBLE:
  428. case FieldDescriptor::TYPE_FLOAT:
  429. type = "float";
  430. break;
  431. case FieldDescriptor::TYPE_BOOL:
  432. type = "bool";
  433. break;
  434. case FieldDescriptor::TYPE_STRING:
  435. case FieldDescriptor::TYPE_BYTES:
  436. type = "string";
  437. break;
  438. case FieldDescriptor::TYPE_MESSAGE:
  439. type = "\\" + FullClassName(field->message_type(), is_descriptor);
  440. break;
  441. case FieldDescriptor::TYPE_GROUP:
  442. return "null";
  443. default: assert(false); return "";
  444. }
  445. if (field->is_repeated()) {
  446. // accommodate for edge case with multiple types.
  447. size_t start_pos = type.find("|");
  448. if (start_pos != std::string::npos) {
  449. type.replace(start_pos, 1, "[]|");
  450. }
  451. type += "[]|\\Google\\Protobuf\\Internal\\RepeatedField";
  452. }
  453. return type;
  454. }
  455. std::string PhpGetterTypeName(const FieldDescriptor* field, bool is_descriptor) {
  456. if (field->is_map()) {
  457. return "\\Google\\Protobuf\\Internal\\MapField";
  458. }
  459. if (field->is_repeated()) {
  460. return "\\Google\\Protobuf\\Internal\\RepeatedField";
  461. }
  462. switch (field->type()) {
  463. case FieldDescriptor::TYPE_INT32:
  464. case FieldDescriptor::TYPE_UINT32:
  465. case FieldDescriptor::TYPE_SINT32:
  466. case FieldDescriptor::TYPE_FIXED32:
  467. case FieldDescriptor::TYPE_SFIXED32:
  468. case FieldDescriptor::TYPE_ENUM: return "int";
  469. case FieldDescriptor::TYPE_INT64:
  470. case FieldDescriptor::TYPE_UINT64:
  471. case FieldDescriptor::TYPE_SINT64:
  472. case FieldDescriptor::TYPE_FIXED64:
  473. case FieldDescriptor::TYPE_SFIXED64: return "int|string";
  474. case FieldDescriptor::TYPE_DOUBLE:
  475. case FieldDescriptor::TYPE_FLOAT: return "float";
  476. case FieldDescriptor::TYPE_BOOL: return "bool";
  477. case FieldDescriptor::TYPE_STRING:
  478. case FieldDescriptor::TYPE_BYTES: return "string";
  479. case FieldDescriptor::TYPE_MESSAGE:
  480. return "\\" + FullClassName(field->message_type(), is_descriptor);
  481. case FieldDescriptor::TYPE_GROUP: return "null";
  482. default: assert(false); return "";
  483. }
  484. }
  485. std::string EnumOrMessageSuffix(
  486. const FieldDescriptor* field, bool is_descriptor) {
  487. if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  488. return ", '" + DescriptorFullName(field->message_type(), is_descriptor) + "'";
  489. }
  490. if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  491. return ", '" + DescriptorFullName(field->enum_type(), is_descriptor) + "'";
  492. }
  493. return "";
  494. }
  495. // Converts a name to camel-case. If cap_first_letter is true, capitalize the
  496. // first letter.
  497. std::string UnderscoresToCamelCase(const string& input, bool cap_first_letter) {
  498. std::string result;
  499. for (int i = 0; i < input.size(); i++) {
  500. if ('a' <= input[i] && input[i] <= 'z') {
  501. if (cap_first_letter) {
  502. result += input[i] + ('A' - 'a');
  503. } else {
  504. result += input[i];
  505. }
  506. cap_first_letter = false;
  507. } else if ('A' <= input[i] && input[i] <= 'Z') {
  508. if (i == 0 && !cap_first_letter) {
  509. // Force first letter to lower-case unless explicitly told to
  510. // capitalize it.
  511. result += input[i] + ('a' - 'A');
  512. } else {
  513. // Capital letters after the first are left as-is.
  514. result += input[i];
  515. }
  516. cap_first_letter = false;
  517. } else if ('0' <= input[i] && input[i] <= '9') {
  518. result += input[i];
  519. cap_first_letter = true;
  520. } else {
  521. cap_first_letter = true;
  522. }
  523. }
  524. // Add a trailing "_" if the name should be altered.
  525. if (input[input.size() - 1] == '#') {
  526. result += '_';
  527. }
  528. return result;
  529. }
  530. std::string EscapeDollor(const string& to_escape) {
  531. return StringReplace(to_escape, "$", "\\$", true);
  532. }
  533. std::string BinaryToHex(const string& src) {
  534. string dest;
  535. size_t i;
  536. unsigned char symbol[16] = {
  537. '0', '1', '2', '3',
  538. '4', '5', '6', '7',
  539. '8', '9', 'a', 'b',
  540. 'c', 'd', 'e', 'f',
  541. };
  542. dest.resize(src.size() * 2);
  543. char* append_ptr = &dest[0];
  544. for (i = 0; i < src.size(); i++) {
  545. *append_ptr++ = symbol[(src[i] & 0xf0) >> 4];
  546. *append_ptr++ = symbol[src[i] & 0x0f];
  547. }
  548. return dest;
  549. }
  550. void Indent(io::Printer* printer) {
  551. printer->Indent();
  552. printer->Indent();
  553. }
  554. void Outdent(io::Printer* printer) {
  555. printer->Outdent();
  556. printer->Outdent();
  557. }
  558. void GenerateField(const FieldDescriptor* field, io::Printer* printer,
  559. bool is_descriptor) {
  560. if (field->is_repeated()) {
  561. GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty);
  562. printer->Print(
  563. "private $^name^;\n",
  564. "name", field->name());
  565. } else if (field->containing_oneof()) {
  566. // Oneof fields are handled by GenerateOneofField.
  567. return;
  568. } else {
  569. GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty);
  570. printer->Print(
  571. "private $^name^ = ^default^;\n",
  572. "name", field->name(),
  573. "default", DefaultForField(field));
  574. }
  575. if (is_descriptor) {
  576. printer->Print(
  577. "private $has_^name^ = false;\n",
  578. "name", field->name());
  579. }
  580. }
  581. void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
  582. // Oneof property needs to be protected in order to be accessed by parent
  583. // class in implementation.
  584. printer->Print(
  585. "protected $^name^;\n",
  586. "name", oneof->name());
  587. }
  588. void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
  589. io::Printer* printer) {
  590. const OneofDescriptor* oneof = field->containing_oneof();
  591. // Generate getter.
  592. if (oneof != NULL) {
  593. GenerateFieldDocComment(printer, field, is_descriptor, kFieldGetter);
  594. printer->Print(
  595. "public function get^camel_name^()\n"
  596. "{\n"
  597. " return $this->readOneof(^number^);\n"
  598. "}\n\n",
  599. "camel_name", UnderscoresToCamelCase(field->name(), true),
  600. "number", IntToString(field->number()));
  601. } else {
  602. GenerateFieldDocComment(printer, field, is_descriptor, kFieldGetter);
  603. printer->Print(
  604. "public function get^camel_name^()\n"
  605. "{\n"
  606. " return $this->^name^;\n"
  607. "}\n\n",
  608. "camel_name", UnderscoresToCamelCase(field->name(), true), "name",
  609. field->name());
  610. }
  611. // Generate setter.
  612. GenerateFieldDocComment(printer, field, is_descriptor, kFieldSetter);
  613. printer->Print(
  614. "public function set^camel_name^($var)\n"
  615. "{\n",
  616. "camel_name", UnderscoresToCamelCase(field->name(), true));
  617. Indent(printer);
  618. // Type check.
  619. if (field->is_map()) {
  620. const Descriptor* map_entry = field->message_type();
  621. const FieldDescriptor* key = map_entry->FindFieldByName("key");
  622. const FieldDescriptor* value = map_entry->FindFieldByName("value");
  623. printer->Print(
  624. "$arr = GPBUtil::checkMapField($var, "
  625. "\\Google\\Protobuf\\Internal\\GPBType::^key_type^, "
  626. "\\Google\\Protobuf\\Internal\\GPBType::^value_type^",
  627. "key_type", ToUpper(key->type_name()),
  628. "value_type", ToUpper(value->type_name()));
  629. if (value->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  630. printer->Print(
  631. ", \\^class_name^);\n",
  632. "class_name",
  633. FullClassName(value->message_type(), is_descriptor) + "::class");
  634. } else if (value->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  635. printer->Print(
  636. ", \\^class_name^);\n",
  637. "class_name",
  638. FullClassName(value->enum_type(), is_descriptor) + "::class");
  639. } else {
  640. printer->Print(");\n");
  641. }
  642. } else if (field->is_repeated()) {
  643. printer->Print(
  644. "$arr = GPBUtil::checkRepeatedField($var, "
  645. "\\Google\\Protobuf\\Internal\\GPBType::^type^",
  646. "type", ToUpper(field->type_name()));
  647. if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  648. printer->Print(
  649. ", \\^class_name^);\n",
  650. "class_name",
  651. FullClassName(field->message_type(), is_descriptor) + "::class");
  652. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  653. printer->Print(
  654. ", \\^class_name^);\n",
  655. "class_name",
  656. FullClassName(field->enum_type(), is_descriptor) + "::class");
  657. } else {
  658. printer->Print(");\n");
  659. }
  660. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  661. printer->Print(
  662. "GPBUtil::checkMessage($var, \\^class_name^::class);\n",
  663. "class_name", LegacyFullClassName(field->message_type(), is_descriptor));
  664. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  665. printer->Print(
  666. "GPBUtil::checkEnum($var, \\^class_name^::class);\n",
  667. "class_name", LegacyFullClassName(field->enum_type(), is_descriptor));
  668. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
  669. printer->Print(
  670. "GPBUtil::checkString($var, ^utf8^);\n",
  671. "utf8",
  672. field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
  673. } else {
  674. printer->Print(
  675. "GPBUtil::check^type^($var);\n",
  676. "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
  677. }
  678. if (oneof != NULL) {
  679. printer->Print(
  680. "$this->writeOneof(^number^, $var);\n",
  681. "number", IntToString(field->number()));
  682. } else if (field->is_repeated()) {
  683. printer->Print(
  684. "$this->^name^ = $arr;\n",
  685. "name", field->name());
  686. } else {
  687. printer->Print(
  688. "$this->^name^ = $var;\n",
  689. "name", field->name());
  690. }
  691. // Set has bit for proto2 only.
  692. if (is_descriptor) {
  693. printer->Print(
  694. "$this->has_^field_name^ = true;\n",
  695. "field_name", field->name());
  696. }
  697. printer->Print("\nreturn $this;\n");
  698. Outdent(printer);
  699. printer->Print(
  700. "}\n\n");
  701. // Generate has method for proto2 only.
  702. if (is_descriptor) {
  703. printer->Print(
  704. "public function has^camel_name^()\n"
  705. "{\n"
  706. " return $this->has_^field_name^;\n"
  707. "}\n\n",
  708. "camel_name", UnderscoresToCamelCase(field->name(), true),
  709. "field_name", field->name());
  710. }
  711. }
  712. void GenerateEnumToPool(const EnumDescriptor* en, io::Printer* printer) {
  713. printer->Print(
  714. "$pool->addEnum('^name^', "
  715. "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
  716. "name", DescriptorFullName(en, true),
  717. "class_name", en->name());
  718. Indent(printer);
  719. for (int i = 0; i < en->value_count(); i++) {
  720. const EnumValueDescriptor* value = en->value(i);
  721. printer->Print(
  722. "->value(\"^name^\", ^number^)\n",
  723. "name", ConstantNamePrefix(value->name()) + value->name(),
  724. "number", IntToString(value->number()));
  725. }
  726. printer->Print("->finalizeToPool();\n\n");
  727. Outdent(printer);
  728. }
  729. void GenerateServiceMethod(const MethodDescriptor* method,
  730. io::Printer* printer) {
  731. printer->Print(
  732. "public function ^camel_name^(\\^request_name^ $request);\n\n",
  733. "camel_name", UnderscoresToCamelCase(method->name(), false),
  734. "request_name", FullClassName(
  735. method->input_type(), false)
  736. );
  737. }
  738. void GenerateMessageToPool(const string& name_prefix, const Descriptor* message,
  739. io::Printer* printer) {
  740. // Don't generate MapEntry messages -- we use the PHP extension's native
  741. // support for map fields instead.
  742. if (message->options().map_entry()) {
  743. return;
  744. }
  745. string class_name = (name_prefix.empty() ? "" : name_prefix + "\\") +
  746. ReservedNamePrefix(message->name(), message->file()) + message->name();
  747. printer->Print(
  748. "$pool->addMessage('^message^', "
  749. "\\Google\\Protobuf\\Internal\\^class_name^::class)\n",
  750. "message", DescriptorFullName(message, true),
  751. "class_name", class_name);
  752. Indent(printer);
  753. for (int i = 0; i < message->field_count(); i++) {
  754. const FieldDescriptor* field = message->field(i);
  755. if (field->is_map()) {
  756. const FieldDescriptor* key =
  757. field->message_type()->FindFieldByName("key");
  758. const FieldDescriptor* val =
  759. field->message_type()->FindFieldByName("value");
  760. printer->Print(
  761. "->map('^field^', \\Google\\Protobuf\\Internal\\GPBType::^key^, "
  762. "\\Google\\Protobuf\\Internal\\GPBType::^value^, ^number^^other^)\n",
  763. "field", field->name(),
  764. "key", ToUpper(key->type_name()),
  765. "value", ToUpper(val->type_name()),
  766. "number", SimpleItoa(field->number()),
  767. "other", EnumOrMessageSuffix(val, true));
  768. } else if (!field->containing_oneof()) {
  769. printer->Print(
  770. "->^label^('^field^', "
  771. "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
  772. "field", field->name(),
  773. "label", LabelForField(field),
  774. "type", ToUpper(field->type_name()),
  775. "number", SimpleItoa(field->number()),
  776. "other", EnumOrMessageSuffix(field, true));
  777. }
  778. }
  779. // oneofs.
  780. for (int i = 0; i < message->oneof_decl_count(); i++) {
  781. const OneofDescriptor* oneof = message->oneof_decl(i);
  782. printer->Print("->oneof(^name^)\n",
  783. "name", oneof->name());
  784. Indent(printer);
  785. for (int index = 0; index < oneof->field_count(); index++) {
  786. const FieldDescriptor* field = oneof->field(index);
  787. printer->Print(
  788. "->value('^field^', "
  789. "\\Google\\Protobuf\\Internal\\GPBType::^type^, ^number^^other^)\n",
  790. "field", field->name(),
  791. "type", ToUpper(field->type_name()),
  792. "number", SimpleItoa(field->number()),
  793. "other", EnumOrMessageSuffix(field, true));
  794. }
  795. printer->Print("->finish()\n");
  796. Outdent(printer);
  797. }
  798. printer->Print(
  799. "->finalizeToPool();\n");
  800. Outdent(printer);
  801. printer->Print(
  802. "\n");
  803. for (int i = 0; i < message->nested_type_count(); i++) {
  804. GenerateMessageToPool(class_name, message->nested_type(i), printer);
  805. }
  806. for (int i = 0; i < message->enum_type_count(); i++) {
  807. GenerateEnumToPool(message->enum_type(i), printer);
  808. }
  809. }
  810. void GenerateAddFileToPool(const FileDescriptor* file, bool is_descriptor,
  811. io::Printer* printer) {
  812. printer->Print(
  813. "public static $is_initialized = false;\n\n"
  814. "public static function initOnce() {\n");
  815. Indent(printer);
  816. printer->Print(
  817. "$pool = \\Google\\Protobuf\\Internal\\"
  818. "DescriptorPool::getGeneratedPool();\n\n"
  819. "if (static::$is_initialized == true) {\n"
  820. " return;\n"
  821. "}\n");
  822. if (is_descriptor) {
  823. for (int i = 0; i < file->message_type_count(); i++) {
  824. GenerateMessageToPool("", file->message_type(i), printer);
  825. }
  826. for (int i = 0; i < file->enum_type_count(); i++) {
  827. GenerateEnumToPool(file->enum_type(i), printer);
  828. }
  829. printer->Print(
  830. "$pool->finish();\n");
  831. } else {
  832. for (int i = 0; i < file->dependency_count(); i++) {
  833. const std::string& name = file->dependency(i)->name();
  834. // Currently, descriptor.proto is not ready for external usage. Skip to
  835. // import it for now, so that its dependencies can still work as long as
  836. // they don't use protos defined in descriptor.proto.
  837. if (name == kDescriptorFile) {
  838. continue;
  839. }
  840. std::string dependency_filename =
  841. GeneratedMetadataFileName(file->dependency(i), is_descriptor);
  842. printer->Print(
  843. "\\^name^::initOnce();\n",
  844. "name", FilenameToClassname(dependency_filename));
  845. }
  846. // Add messages and enums to descriptor pool.
  847. FileDescriptorSet files;
  848. FileDescriptorProto* file_proto = files.add_file();
  849. file->CopyTo(file_proto);
  850. // Filter out descriptor.proto as it cannot be depended on for now.
  851. RepeatedPtrField<string>* dependency = file_proto->mutable_dependency();
  852. for (RepeatedPtrField<string>::iterator it = dependency->begin();
  853. it != dependency->end(); ++it) {
  854. if (*it != kDescriptorFile) {
  855. dependency->erase(it);
  856. break;
  857. }
  858. }
  859. // Filter out all extensions, since we do not support extension yet.
  860. file_proto->clear_extension();
  861. RepeatedPtrField<DescriptorProto>* message_type =
  862. file_proto->mutable_message_type();
  863. for (RepeatedPtrField<DescriptorProto>::iterator it = message_type->begin();
  864. it != message_type->end(); ++it) {
  865. it->clear_extension();
  866. }
  867. string files_data;
  868. files.SerializeToString(&files_data);
  869. printer->Print("$pool->internalAddGeneratedFile(hex2bin(\n");
  870. Indent(printer);
  871. // Only write 30 bytes per line.
  872. static const int kBytesPerLine = 30;
  873. for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
  874. printer->Print(
  875. "\"^data^\"^dot^\n",
  876. "data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
  877. "dot", i + kBytesPerLine < files_data.size() ? " ." : "");
  878. }
  879. Outdent(printer);
  880. printer->Print(
  881. "));\n\n");
  882. }
  883. printer->Print(
  884. "static::$is_initialized = true;\n");
  885. Outdent(printer);
  886. printer->Print("}\n");
  887. }
  888. void GenerateUseDeclaration(bool is_descriptor, io::Printer* printer) {
  889. if (!is_descriptor) {
  890. printer->Print(
  891. "use Google\\Protobuf\\Internal\\GPBType;\n"
  892. "use Google\\Protobuf\\Internal\\RepeatedField;\n"
  893. "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
  894. } else {
  895. printer->Print(
  896. "use Google\\Protobuf\\Internal\\GPBType;\n"
  897. "use Google\\Protobuf\\Internal\\GPBWire;\n"
  898. "use Google\\Protobuf\\Internal\\RepeatedField;\n"
  899. "use Google\\Protobuf\\Internal\\InputStream;\n"
  900. "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
  901. }
  902. }
  903. void GenerateHead(const FileDescriptor* file, io::Printer* printer) {
  904. printer->Print(
  905. "<?php\n"
  906. "# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
  907. "# source: ^filename^\n"
  908. "\n",
  909. "filename", file->name());
  910. }
  911. std::string FilenameToClassname(const string& filename) {
  912. int lastindex = filename.find_last_of(".");
  913. std::string result = filename.substr(0, lastindex);
  914. for (int i = 0; i < result.size(); i++) {
  915. if (result[i] == '/') {
  916. result[i] = '\\';
  917. }
  918. }
  919. return result;
  920. }
  921. void GenerateMetadataFile(const FileDescriptor* file,
  922. bool is_descriptor,
  923. GeneratorContext* generator_context) {
  924. std::string filename = GeneratedMetadataFileName(file, is_descriptor);
  925. std::unique_ptr<io::ZeroCopyOutputStream> output(
  926. generator_context->Open(filename));
  927. io::Printer printer(output.get(), '^');
  928. GenerateHead(file, &printer);
  929. std::string fullname = FilenameToClassname(filename);
  930. int lastindex = fullname.find_last_of("\\");
  931. if (lastindex != string::npos) {
  932. printer.Print(
  933. "namespace ^name^;\n\n",
  934. "name", fullname.substr(0, lastindex));
  935. printer.Print(
  936. "class ^name^\n"
  937. "{\n",
  938. "name", fullname.substr(lastindex + 1));
  939. } else {
  940. printer.Print(
  941. "class ^name^\n"
  942. "{\n",
  943. "name", fullname);
  944. }
  945. Indent(&printer);
  946. GenerateAddFileToPool(file, is_descriptor, &printer);
  947. Outdent(&printer);
  948. printer.Print("}\n\n");
  949. }
  950. template <typename DescriptorType>
  951. void LegacyGenerateClassFile(const FileDescriptor* file, const DescriptorType* desc,
  952. bool is_descriptor,
  953. GeneratorContext* generator_context) {
  954. std::string filename = LegacyGeneratedClassFileName(desc, is_descriptor);
  955. std::unique_ptr<io::ZeroCopyOutputStream> output(
  956. generator_context->Open(filename));
  957. io::Printer printer(output.get(), '^');
  958. GenerateHead(file, &printer);
  959. std::string php_namespace = RootPhpNamespace(desc, is_descriptor);
  960. if (php_namespace != "") {
  961. printer.Print(
  962. "namespace ^name^;\n\n",
  963. "name", php_namespace);
  964. }
  965. std::string newname = FullClassName(desc, is_descriptor);
  966. printer.Print("if (false) {\n");
  967. Indent(&printer);
  968. printer.Print("/**\n");
  969. printer.Print(" * This class is deprecated. Use ^new^ instead.\n",
  970. "new", newname);
  971. printer.Print(" * @deprecated\n");
  972. printer.Print(" */\n");
  973. printer.Print("class ^old^ {}\n",
  974. "old", LegacyGeneratedClassName(desc));
  975. Outdent(&printer);
  976. printer.Print("}\n");
  977. printer.Print("class_exists(^new^::class);\n",
  978. "new", GeneratedClassNameImpl(desc));
  979. printer.Print("@trigger_error('^old^ is deprecated and will be removed in "
  980. "the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
  981. "old", LegacyFullClassName(desc, is_descriptor),
  982. "fullname", newname);
  983. }
  984. void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
  985. bool is_descriptor, GeneratorContext* generator_context) {
  986. std::string filename = GeneratedClassFileName(en, is_descriptor);
  987. std::unique_ptr<io::ZeroCopyOutputStream> output(
  988. generator_context->Open(filename));
  989. io::Printer printer(output.get(), '^');
  990. GenerateHead(file, &printer);
  991. std::string fullname = FilenameToClassname(filename);
  992. int lastindex = fullname.find_last_of("\\");
  993. if (lastindex != string::npos) {
  994. printer.Print(
  995. "namespace ^name^;\n\n",
  996. "name", fullname.substr(0, lastindex));
  997. }
  998. if (lastindex != string::npos) {
  999. fullname = fullname.substr(lastindex + 1);
  1000. }
  1001. GenerateEnumDocComment(&printer, en, is_descriptor);
  1002. printer.Print(
  1003. "class ^name^\n"
  1004. "{\n",
  1005. "name", fullname);
  1006. Indent(&printer);
  1007. for (int i = 0; i < en->value_count(); i++) {
  1008. const EnumValueDescriptor* value = en->value(i);
  1009. GenerateEnumValueDocComment(&printer, value);
  1010. printer.Print("const ^name^ = ^number^;\n",
  1011. "name", ConstantNamePrefix(value->name()) + value->name(),
  1012. "number", IntToString(value->number()));
  1013. }
  1014. Outdent(&printer);
  1015. printer.Print("}\n\n");
  1016. // write legacy file for backwards compatiblity with nested messages and enums
  1017. if (en->containing_type() != NULL) {
  1018. printer.Print(
  1019. "// Adding a class alias for backwards compatibility with the previous class name.\n");
  1020. printer.Print(
  1021. "class_alias(^new^::class, \\^old^::class);\n\n",
  1022. "new", fullname,
  1023. "old", LegacyFullClassName(en, is_descriptor));
  1024. LegacyGenerateClassFile(file, en, is_descriptor, generator_context);
  1025. }
  1026. }
  1027. void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
  1028. bool is_descriptor,
  1029. GeneratorContext* generator_context) {
  1030. // Don't generate MapEntry messages -- we use the PHP extension's native
  1031. // support for map fields instead.
  1032. if (message->options().map_entry()) {
  1033. return;
  1034. }
  1035. std::string filename = GeneratedClassFileName(message, is_descriptor);
  1036. std::unique_ptr<io::ZeroCopyOutputStream> output(
  1037. generator_context->Open(filename));
  1038. io::Printer printer(output.get(), '^');
  1039. GenerateHead(file, &printer);
  1040. std::string fullname = FilenameToClassname(filename);
  1041. int lastindex = fullname.find_last_of("\\");
  1042. if (lastindex != string::npos) {
  1043. printer.Print(
  1044. "namespace ^name^;\n\n",
  1045. "name", fullname.substr(0, lastindex));
  1046. }
  1047. GenerateUseDeclaration(is_descriptor, &printer);
  1048. GenerateMessageDocComment(&printer, message, is_descriptor);
  1049. if (lastindex != string::npos) {
  1050. fullname = fullname.substr(lastindex + 1);
  1051. }
  1052. printer.Print(
  1053. "class ^name^ extends \\Google\\Protobuf\\Internal\\Message\n"
  1054. "{\n",
  1055. "name", fullname);
  1056. Indent(&printer);
  1057. // Field and oneof definitions.
  1058. for (int i = 0; i < message->field_count(); i++) {
  1059. const FieldDescriptor* field = message->field(i);
  1060. GenerateField(field, &printer, is_descriptor);
  1061. }
  1062. for (int i = 0; i < message->oneof_decl_count(); i++) {
  1063. const OneofDescriptor* oneof = message->oneof_decl(i);
  1064. GenerateOneofField(oneof, &printer);
  1065. }
  1066. printer.Print("\n");
  1067. GenerateMessageConstructorDocComment(&printer, message, is_descriptor);
  1068. printer.Print(
  1069. "public function __construct($data = NULL) {\n");
  1070. Indent(&printer);
  1071. std::string metadata_filename =
  1072. GeneratedMetadataFileName(file, is_descriptor);
  1073. std::string metadata_fullname = FilenameToClassname(metadata_filename);
  1074. printer.Print(
  1075. "\\^fullname^::initOnce();\n"
  1076. "parent::__construct($data);\n",
  1077. "fullname", metadata_fullname);
  1078. Outdent(&printer);
  1079. printer.Print("}\n\n");
  1080. // Field and oneof accessors.
  1081. for (int i = 0; i < message->field_count(); i++) {
  1082. const FieldDescriptor* field = message->field(i);
  1083. GenerateFieldAccessor(field, is_descriptor, &printer);
  1084. }
  1085. for (int i = 0; i < message->oneof_decl_count(); i++) {
  1086. const OneofDescriptor* oneof = message->oneof_decl(i);
  1087. printer.Print(
  1088. "/**\n"
  1089. " * @return string\n"
  1090. " */\n"
  1091. "public function get^camel_name^()\n"
  1092. "{\n"
  1093. " return $this->whichOneof(\"^name^\");\n"
  1094. "}\n\n",
  1095. "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
  1096. oneof->name());
  1097. }
  1098. Outdent(&printer);
  1099. printer.Print("}\n\n");
  1100. // write legacy file for backwards compatiblity with nested messages and enums
  1101. if (message->containing_type() != NULL) {
  1102. printer.Print(
  1103. "// Adding a class alias for backwards compatibility with the previous class name.\n");
  1104. printer.Print(
  1105. "class_alias(^new^::class, \\^old^::class);\n\n",
  1106. "new", fullname,
  1107. "old", LegacyFullClassName(message, is_descriptor));
  1108. LegacyGenerateClassFile(file, message, is_descriptor, generator_context);
  1109. }
  1110. // Nested messages and enums.
  1111. for (int i = 0; i < message->nested_type_count(); i++) {
  1112. GenerateMessageFile(file, message->nested_type(i), is_descriptor,
  1113. generator_context);
  1114. }
  1115. for (int i = 0; i < message->enum_type_count(); i++) {
  1116. GenerateEnumFile(file, message->enum_type(i), is_descriptor,
  1117. generator_context);
  1118. }
  1119. }
  1120. void GenerateServiceFile(const FileDescriptor* file,
  1121. const ServiceDescriptor* service, bool is_descriptor,
  1122. GeneratorContext* generator_context) {
  1123. std::string filename = GeneratedServiceFileName(service, is_descriptor);
  1124. std::unique_ptr<io::ZeroCopyOutputStream> output(
  1125. generator_context->Open(filename));
  1126. io::Printer printer(output.get(), '^');
  1127. GenerateHead(file, &printer);
  1128. std::string fullname = FilenameToClassname(filename);
  1129. int lastindex = fullname.find_last_of("\\");
  1130. if (!file->options().php_namespace().empty() ||
  1131. (!file->options().has_php_namespace() && !file->package().empty()) ||
  1132. lastindex != string::npos) {
  1133. printer.Print(
  1134. "namespace ^name^;\n\n",
  1135. "name", fullname.substr(0, lastindex));
  1136. }
  1137. GenerateServiceDocComment(&printer, service);
  1138. if (lastindex != string::npos) {
  1139. printer.Print(
  1140. "interface ^name^\n"
  1141. "{\n",
  1142. "name", fullname.substr(lastindex + 1));
  1143. } else {
  1144. printer.Print(
  1145. "interface ^name^\n"
  1146. "{\n",
  1147. "name", fullname);
  1148. }
  1149. Indent(&printer);
  1150. for (int i = 0; i < service->method_count(); i++) {
  1151. const MethodDescriptor* method = service->method(i);
  1152. GenerateServiceMethodDocComment(&printer, method);
  1153. GenerateServiceMethod(method, &printer);
  1154. }
  1155. Outdent(&printer);
  1156. printer.Print("}\n\n");
  1157. }
  1158. void GenerateFile(const FileDescriptor* file, bool is_descriptor,
  1159. GeneratorContext* generator_context) {
  1160. GenerateMetadataFile(file, is_descriptor, generator_context);
  1161. for (int i = 0; i < file->message_type_count(); i++) {
  1162. GenerateMessageFile(file, file->message_type(i), is_descriptor,
  1163. generator_context);
  1164. }
  1165. for (int i = 0; i < file->enum_type_count(); i++) {
  1166. GenerateEnumFile(file, file->enum_type(i), is_descriptor,
  1167. generator_context);
  1168. }
  1169. if (file->options().php_generic_services()) {
  1170. for (int i = 0; i < file->service_count(); i++) {
  1171. GenerateServiceFile(file, file->service(i), is_descriptor,
  1172. generator_context);
  1173. }
  1174. }
  1175. }
  1176. static string EscapePhpdoc(const string& input) {
  1177. string result;
  1178. result.reserve(input.size() * 2);
  1179. char prev = '*';
  1180. for (string::size_type i = 0; i < input.size(); i++) {
  1181. char c = input[i];
  1182. switch (c) {
  1183. case '*':
  1184. // Avoid "/*".
  1185. if (prev == '/') {
  1186. result.append("&#42;");
  1187. } else {
  1188. result.push_back(c);
  1189. }
  1190. break;
  1191. case '/':
  1192. // Avoid "*/".
  1193. if (prev == '*') {
  1194. result.append("&#47;");
  1195. } else {
  1196. result.push_back(c);
  1197. }
  1198. break;
  1199. case '@':
  1200. // '@' starts phpdoc tags including the @deprecated tag, which will
  1201. // cause a compile-time error if inserted before a declaration that
  1202. // does not have a corresponding @Deprecated annotation.
  1203. result.append("&#64;");
  1204. break;
  1205. default:
  1206. result.push_back(c);
  1207. break;
  1208. }
  1209. prev = c;
  1210. }
  1211. return result;
  1212. }
  1213. static void GenerateDocCommentBodyForLocation(
  1214. io::Printer* printer, const SourceLocation& location, bool trailingNewline,
  1215. int indentCount) {
  1216. string comments = location.leading_comments.empty() ?
  1217. location.trailing_comments : location.leading_comments;
  1218. if (!comments.empty()) {
  1219. // TODO(teboring): Ideally we should parse the comment text as Markdown and
  1220. // write it back as HTML, but this requires a Markdown parser. For now
  1221. // we just use the proto comments unchanged.
  1222. // If the comment itself contains block comment start or end markers,
  1223. // HTML-escape them so that they don't accidentally close the doc comment.
  1224. comments = EscapePhpdoc(comments);
  1225. std::vector<string> lines = Split(comments, "\n");
  1226. while (!lines.empty() && lines.back().empty()) {
  1227. lines.pop_back();
  1228. }
  1229. for (int i = 0; i < lines.size(); i++) {
  1230. // Most lines should start with a space. Watch out for lines that start
  1231. // with a /, since putting that right after the leading asterisk will
  1232. // close the comment.
  1233. if (indentCount == 0 && !lines[i].empty() && lines[i][0] == '/') {
  1234. printer->Print(" * ^line^\n", "line", lines[i]);
  1235. } else {
  1236. std::string indent = std::string(indentCount, ' ');
  1237. printer->Print(" *^ind^^line^\n", "ind", indent, "line", lines[i]);
  1238. }
  1239. }
  1240. if (trailingNewline) {
  1241. printer->Print(" *\n");
  1242. }
  1243. }
  1244. }
  1245. template <typename DescriptorType>
  1246. static void GenerateDocCommentBody(
  1247. io::Printer* printer, const DescriptorType* descriptor) {
  1248. SourceLocation location;
  1249. if (descriptor->GetSourceLocation(&location)) {
  1250. GenerateDocCommentBodyForLocation(printer, location, true, 0);
  1251. }
  1252. }
  1253. static string FirstLineOf(const string& value) {
  1254. string result = value;
  1255. string::size_type pos = result.find_first_of('\n');
  1256. if (pos != string::npos) {
  1257. result.erase(pos);
  1258. }
  1259. return result;
  1260. }
  1261. void GenerateMessageDocComment(io::Printer* printer,
  1262. const Descriptor* message, int is_descriptor) {
  1263. printer->Print("/**\n");
  1264. GenerateDocCommentBody(printer, message);
  1265. printer->Print(
  1266. " * Generated from protobuf message <code>^messagename^</code>\n"
  1267. " */\n",
  1268. "fullname", EscapePhpdoc(FullClassName(message, is_descriptor)),
  1269. "messagename", EscapePhpdoc(message->full_name()));
  1270. }
  1271. void GenerateMessageConstructorDocComment(io::Printer* printer,
  1272. const Descriptor* message,
  1273. int is_descriptor) {
  1274. // In theory we should have slightly different comments for setters, getters,
  1275. // etc., but in practice everyone already knows the difference between these
  1276. // so it's redundant information.
  1277. // We start the comment with the main body based on the comments from the
  1278. // .proto file (if present). We then end with the field declaration, e.g.:
  1279. // optional string foo = 5;
  1280. // If the field is a group, the debug string might end with {.
  1281. printer->Print("/**\n");
  1282. printer->Print(" * Constructor.\n");
  1283. printer->Print(" *\n");
  1284. printer->Print(" * @param array $data {\n");
  1285. printer->Print(" * Optional. Data for populating the Message object.\n");
  1286. printer->Print(" *\n");
  1287. for (int i = 0; i < message->field_count(); i++) {
  1288. const FieldDescriptor* field = message->field(i);
  1289. printer->Print(" * @type ^php_type^ $^var^\n",
  1290. "php_type", PhpSetterTypeName(field, is_descriptor),
  1291. "var", field->name());
  1292. SourceLocation location;
  1293. if (field->GetSourceLocation(&location)) {
  1294. GenerateDocCommentBodyForLocation(printer, location, false, 10);
  1295. }
  1296. }
  1297. printer->Print(" * }\n");
  1298. printer->Print(" */\n");
  1299. }
  1300. void GenerateServiceDocComment(io::Printer* printer,
  1301. const ServiceDescriptor* service) {
  1302. printer->Print("/**\n");
  1303. GenerateDocCommentBody(printer, service);
  1304. printer->Print(
  1305. " * Protobuf type <code>^fullname^</code>\n"
  1306. " */\n",
  1307. "fullname", EscapePhpdoc(service->full_name()));
  1308. }
  1309. void GenerateFieldDocComment(io::Printer* printer, const FieldDescriptor* field,
  1310. int is_descriptor, int function_type) {
  1311. // In theory we should have slightly different comments for setters, getters,
  1312. // etc., but in practice everyone already knows the difference between these
  1313. // so it's redundant information.
  1314. // We start the comment with the main body based on the comments from the
  1315. // .proto file (if present). We then end with the field declaration, e.g.:
  1316. // optional string foo = 5;
  1317. // If the field is a group, the debug string might end with {.
  1318. printer->Print("/**\n");
  1319. GenerateDocCommentBody(printer, field);
  1320. printer->Print(
  1321. " * Generated from protobuf field <code>^def^</code>\n",
  1322. "def", EscapePhpdoc(FirstLineOf(field->DebugString())));
  1323. if (function_type == kFieldSetter) {
  1324. printer->Print(" * @param ^php_type^ $var\n",
  1325. "php_type", PhpSetterTypeName(field, is_descriptor));
  1326. printer->Print(" * @return $this\n");
  1327. } else if (function_type == kFieldGetter) {
  1328. printer->Print(" * @return ^php_type^\n",
  1329. "php_type", PhpGetterTypeName(field, is_descriptor));
  1330. }
  1331. printer->Print(" */\n");
  1332. }
  1333. void GenerateEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_,
  1334. int is_descriptor) {
  1335. printer->Print("/**\n");
  1336. GenerateDocCommentBody(printer, enum_);
  1337. printer->Print(
  1338. " * Protobuf type <code>^fullname^</code>\n"
  1339. " */\n",
  1340. "fullname", EscapePhpdoc(enum_->full_name()));
  1341. }
  1342. void GenerateEnumValueDocComment(io::Printer* printer,
  1343. const EnumValueDescriptor* value) {
  1344. printer->Print("/**\n");
  1345. GenerateDocCommentBody(printer, value);
  1346. printer->Print(
  1347. " * Generated from protobuf enum <code>^def^</code>\n"
  1348. " */\n",
  1349. "def", EscapePhpdoc(FirstLineOf(value->DebugString())));
  1350. }
  1351. void GenerateServiceMethodDocComment(io::Printer* printer,
  1352. const MethodDescriptor* method) {
  1353. printer->Print("/**\n");
  1354. GenerateDocCommentBody(printer, method);
  1355. printer->Print(
  1356. " * Method <code>^method_name^</code>\n"
  1357. " *\n",
  1358. "method_name", EscapePhpdoc(UnderscoresToCamelCase(method->name(), false)));
  1359. printer->Print(
  1360. " * @param \\^input_type^ $request\n",
  1361. "input_type", EscapePhpdoc(FullClassName(method->input_type(), false)));
  1362. printer->Print(
  1363. " * @return \\^return_type^\n"
  1364. " */\n",
  1365. "return_type", EscapePhpdoc(FullClassName(method->output_type(), false)));
  1366. }
  1367. bool Generator::Generate(const FileDescriptor* file, const string& parameter,
  1368. GeneratorContext* generator_context,
  1369. string* error) const {
  1370. bool is_descriptor = parameter == "internal";
  1371. if (is_descriptor && file->name() != kDescriptorFile) {
  1372. *error =
  1373. "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
  1374. return false;
  1375. }
  1376. if (!is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
  1377. *error =
  1378. "Can only generate PHP code for proto3 .proto files.\n"
  1379. "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
  1380. return false;
  1381. }
  1382. GenerateFile(file, is_descriptor, generator_context);
  1383. return true;
  1384. }
  1385. } // namespace php
  1386. } // namespace compiler
  1387. } // namespace protobuf
  1388. } // namespace google