convert.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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 "convert.h"
  31. #include <php.h>
  32. // This is not self-contained: it must be after other Zend includes.
  33. #include <Zend/zend_exceptions.h>
  34. #include "array.h"
  35. #include "map.h"
  36. #include "message.h"
  37. #include "php-upb.h"
  38. #include "protobuf.h"
  39. // -----------------------------------------------------------------------------
  40. // GPBUtil
  41. // -----------------------------------------------------------------------------
  42. static zend_class_entry* GPBUtil_class_entry;
  43. // The implementation of type checking for primitive fields is empty. This is
  44. // because type checking is done when direct assigning message fields (e.g.,
  45. // foo->a = 1). Functions defined here are place holders in generated code for
  46. // pure PHP implementation (c extension and pure PHP share the same generated
  47. // code).
  48. PHP_METHOD(Util, checkInt32) {}
  49. PHP_METHOD(Util, checkUint32) {}
  50. PHP_METHOD(Util, checkInt64) {}
  51. PHP_METHOD(Util, checkUint64) {}
  52. PHP_METHOD(Util, checkEnum) {}
  53. PHP_METHOD(Util, checkFloat) {}
  54. PHP_METHOD(Util, checkDouble) {}
  55. PHP_METHOD(Util, checkBool) {}
  56. PHP_METHOD(Util, checkString) {}
  57. PHP_METHOD(Util, checkBytes) {}
  58. PHP_METHOD(Util, checkMessage) {}
  59. // The result of checkMapField() is assigned, so we need to return the first
  60. // param:
  61. // $arr = GPBUtil::checkMapField($var,
  62. // \Google\Protobuf\Internal\GPBType::INT64,
  63. // \Google\Protobuf\Internal\GPBType::INT32);
  64. PHP_METHOD(Util, checkMapField) {
  65. zval *val, *key_type, *val_type, *klass;
  66. if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z", &val, &key_type,
  67. &val_type, &klass) == FAILURE) {
  68. return;
  69. }
  70. RETURN_ZVAL(val, 1, 0);
  71. }
  72. // The result of checkRepeatedField() is assigned, so we need to return the
  73. // first param:
  74. // $arr = GPBUtil::checkRepeatedField(
  75. // $var, \Google\Protobuf\Internal\GPBType::STRING);
  76. PHP_METHOD(Util, checkRepeatedField) {
  77. zval *val, *type, *klass;
  78. if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &val, &type, &klass) ==
  79. FAILURE) {
  80. return;
  81. }
  82. RETURN_ZVAL(val, 1, 0);
  83. }
  84. static zend_function_entry util_methods[] = {
  85. PHP_ME(Util, checkInt32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  86. PHP_ME(Util, checkUint32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  87. PHP_ME(Util, checkInt64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  88. PHP_ME(Util, checkUint64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  89. PHP_ME(Util, checkEnum, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  90. PHP_ME(Util, checkFloat, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  91. PHP_ME(Util, checkDouble, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  92. PHP_ME(Util, checkBool, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  93. PHP_ME(Util, checkString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  94. PHP_ME(Util, checkBytes, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  95. PHP_ME(Util, checkMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  96. PHP_ME(Util, checkMapField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  97. PHP_ME(Util, checkRepeatedField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  98. ZEND_FE_END
  99. };
  100. // -----------------------------------------------------------------------------
  101. // Conversion functions used from C
  102. // -----------------------------------------------------------------------------
  103. upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type) {
  104. switch (type) {
  105. #define CASE(descriptor_type, type) \
  106. case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
  107. return UPB_TYPE_##type;
  108. CASE(FLOAT, FLOAT);
  109. CASE(DOUBLE, DOUBLE);
  110. CASE(BOOL, BOOL);
  111. CASE(STRING, STRING);
  112. CASE(BYTES, BYTES);
  113. CASE(MESSAGE, MESSAGE);
  114. CASE(GROUP, MESSAGE);
  115. CASE(ENUM, ENUM);
  116. CASE(INT32, INT32);
  117. CASE(INT64, INT64);
  118. CASE(UINT32, UINT32);
  119. CASE(UINT64, UINT64);
  120. CASE(SINT32, INT32);
  121. CASE(SINT64, INT64);
  122. CASE(FIXED32, UINT32);
  123. CASE(FIXED64, UINT64);
  124. CASE(SFIXED32, INT32);
  125. CASE(SFIXED64, INT64);
  126. #undef CASE
  127. }
  128. zend_error(E_ERROR, "Unknown field type.");
  129. return 0;
  130. }
  131. static bool buftouint64(const char *ptr, const char *end, uint64_t *val) {
  132. uint64_t u64 = 0;
  133. while (ptr < end) {
  134. unsigned ch = (unsigned)(*ptr - '0');
  135. if (ch >= 10) break;
  136. if (u64 > UINT64_MAX / 10 || u64 * 10 > UINT64_MAX - ch) {
  137. return false;
  138. }
  139. u64 *= 10;
  140. u64 += ch;
  141. ptr++;
  142. }
  143. if (ptr != end) {
  144. // In PHP tradition, we allow truncation: "1.1" -> 1.
  145. // But we don't allow 'e', eg. '1.1e2' or any other non-numeric chars.
  146. if (*ptr++ != '.') return false;
  147. for (;ptr < end; ptr++) {
  148. if (*ptr < '0' || *ptr > '9') {
  149. return false;
  150. }
  151. }
  152. }
  153. *val = u64;
  154. return true;
  155. }
  156. static bool buftoint64(const char *ptr, const char *end, int64_t *val) {
  157. bool neg = false;
  158. uint64_t u64;
  159. if (ptr != end && *ptr == '-') {
  160. ptr++;
  161. neg = true;
  162. }
  163. if (!buftouint64(ptr, end, &u64) ||
  164. u64 > (uint64_t)INT64_MAX + neg) {
  165. return false;
  166. }
  167. *val = neg ? -u64 : u64;
  168. return true;
  169. }
  170. static void throw_conversion_exception(const char *to, const zval *zv) {
  171. zval tmp;
  172. ZVAL_COPY(&tmp, zv);
  173. convert_to_string(&tmp);
  174. zend_throw_exception_ex(NULL, 0, "Cannot convert '%s' to %s",
  175. Z_STRVAL_P(&tmp), to);
  176. zval_ptr_dtor(&tmp);
  177. }
  178. bool Convert_PhpToInt64(const zval *php_val, int64_t *i64) {
  179. switch (Z_TYPE_P(php_val)) {
  180. case IS_LONG:
  181. *i64 = Z_LVAL_P(php_val);
  182. return true;
  183. case IS_DOUBLE: {
  184. double dbl = Z_DVAL_P(php_val);
  185. if (dbl > 9223372036854774784.0 || dbl < -9223372036854775808.0) {
  186. zend_throw_exception_ex(NULL, 0, "Out of range");
  187. return false;
  188. }
  189. *i64 = dbl; /* must be guarded, overflow here is UB */
  190. return true;
  191. }
  192. case IS_STRING: {
  193. const char *buf = Z_STRVAL_P(php_val);
  194. // PHP would accept scientific notation here, but we're going to be a
  195. // little more discerning and only accept pure integers.
  196. bool ok = buftoint64(buf, buf + Z_STRLEN_P(php_val), i64);
  197. if (!ok) {
  198. throw_conversion_exception("integer", php_val);
  199. }
  200. return ok;
  201. }
  202. default:
  203. throw_conversion_exception("integer", php_val);
  204. return false;
  205. }
  206. }
  207. static bool to_double(zval *php_val, double *dbl) {
  208. switch (Z_TYPE_P(php_val)) {
  209. case IS_LONG:
  210. *dbl = Z_LVAL_P(php_val);
  211. return true;
  212. case IS_DOUBLE:
  213. *dbl = Z_DVAL_P(php_val);
  214. return true;
  215. case IS_STRING: {
  216. zend_long lval;
  217. switch (is_numeric_string(Z_STRVAL_P(php_val), Z_STRLEN_P(php_val), &lval,
  218. dbl, false)) {
  219. case IS_LONG:
  220. *dbl = lval;
  221. return true;
  222. case IS_DOUBLE:
  223. return true;
  224. default:
  225. goto fail;
  226. }
  227. }
  228. default:
  229. fail:
  230. throw_conversion_exception("double", php_val);
  231. return false;
  232. }
  233. }
  234. static bool to_bool(zval* from, bool* to) {
  235. switch (Z_TYPE_P(from)) {
  236. case IS_TRUE:
  237. *to = true;
  238. return true;
  239. case IS_FALSE:
  240. *to = false;
  241. return true;
  242. case IS_LONG:
  243. *to = (Z_LVAL_P(from) != 0);
  244. return true;
  245. case IS_DOUBLE:
  246. *to = (Z_LVAL_P(from) != 0);
  247. return true;
  248. case IS_STRING:
  249. if (Z_STRLEN_P(from) == 0 ||
  250. (Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) {
  251. *to = false;
  252. } else {
  253. *to = true;
  254. }
  255. return true;
  256. default:
  257. throw_conversion_exception("bool", from);
  258. return false;
  259. }
  260. }
  261. static bool to_string(zval* from) {
  262. if (Z_ISREF_P(from)) {
  263. ZVAL_DEREF(from);
  264. }
  265. switch (Z_TYPE_P(from)) {
  266. case IS_STRING:
  267. return true;
  268. case IS_TRUE:
  269. case IS_FALSE:
  270. case IS_LONG:
  271. case IS_DOUBLE: {
  272. zval tmp;
  273. zend_make_printable_zval(from, &tmp);
  274. ZVAL_COPY_VALUE(from, &tmp);
  275. return true;
  276. }
  277. default:
  278. throw_conversion_exception("string", from);
  279. return false;
  280. }
  281. }
  282. bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type,
  283. const Descriptor *desc, upb_arena *arena) {
  284. int64_t i64;
  285. if (Z_ISREF_P(php_val)) {
  286. ZVAL_DEREF(php_val);
  287. }
  288. switch (type) {
  289. case UPB_TYPE_INT64:
  290. return Convert_PhpToInt64(php_val, &upb_val->int64_val);
  291. case UPB_TYPE_INT32:
  292. case UPB_TYPE_ENUM:
  293. if (!Convert_PhpToInt64(php_val, &i64)) {
  294. return false;
  295. }
  296. upb_val->int32_val = i64;
  297. return true;
  298. case UPB_TYPE_UINT64:
  299. if (!Convert_PhpToInt64(php_val, &i64)) {
  300. return false;
  301. }
  302. upb_val->uint64_val = i64;
  303. return true;
  304. case UPB_TYPE_UINT32:
  305. if (!Convert_PhpToInt64(php_val, &i64)) {
  306. return false;
  307. }
  308. upb_val->uint32_val = i64;
  309. return true;
  310. case UPB_TYPE_DOUBLE:
  311. return to_double(php_val, &upb_val->double_val);
  312. case UPB_TYPE_FLOAT:
  313. if (!to_double(php_val, &upb_val->double_val)) return false;
  314. upb_val->float_val = upb_val->double_val;
  315. return true;
  316. case UPB_TYPE_BOOL:
  317. return to_bool(php_val, &upb_val->bool_val);
  318. case UPB_TYPE_STRING:
  319. case UPB_TYPE_BYTES: {
  320. char *ptr;
  321. size_t size;
  322. if (!to_string(php_val)) return false;
  323. size = Z_STRLEN_P(php_val);
  324. // If arena is NULL we reference the input zval.
  325. // The resulting upb_strview will only be value while the zval is alive.
  326. if (arena) {
  327. ptr = upb_arena_malloc(arena, size);
  328. memcpy(ptr, Z_STRVAL_P(php_val), size);
  329. } else {
  330. ptr = Z_STRVAL_P(php_val);
  331. }
  332. upb_val->str_val = upb_strview_make(ptr, size);
  333. return true;
  334. }
  335. case UPB_TYPE_MESSAGE:
  336. PBPHP_ASSERT(desc);
  337. return Message_GetUpbMessage(php_val, desc, arena,
  338. (upb_msg **)&upb_val->msg_val);
  339. }
  340. return false;
  341. }
  342. void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type,
  343. const Descriptor *desc, zval *arena) {
  344. switch (type) {
  345. case UPB_TYPE_INT64:
  346. #if SIZEOF_ZEND_LONG == 8
  347. ZVAL_LONG(php_val, upb_val.int64_val);
  348. #else
  349. {
  350. char buf[20];
  351. int size = sprintf(buf, "%lld", upb_val.int64_val);
  352. ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0));
  353. }
  354. #endif
  355. break;
  356. case UPB_TYPE_UINT64:
  357. #if SIZEOF_ZEND_LONG == 8
  358. ZVAL_LONG(php_val, upb_val.uint64_val);
  359. #else
  360. {
  361. char buf[20];
  362. int size = sprintf(buf, "%lld", (int64_t)upb_val.uint64_val);
  363. ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0));
  364. }
  365. #endif
  366. break;
  367. case UPB_TYPE_INT32:
  368. case UPB_TYPE_ENUM:
  369. ZVAL_LONG(php_val, upb_val.int32_val);
  370. break;
  371. case UPB_TYPE_UINT32: {
  372. // Sign-extend for consistency between 32/64-bit builds.
  373. zend_long val = (int32_t)upb_val.uint32_val;
  374. ZVAL_LONG(php_val, val);
  375. break;
  376. }
  377. case UPB_TYPE_DOUBLE:
  378. ZVAL_DOUBLE(php_val, upb_val.double_val);
  379. break;
  380. case UPB_TYPE_FLOAT:
  381. ZVAL_DOUBLE(php_val, upb_val.float_val);
  382. break;
  383. case UPB_TYPE_BOOL:
  384. ZVAL_BOOL(php_val, upb_val.bool_val);
  385. break;
  386. case UPB_TYPE_STRING:
  387. case UPB_TYPE_BYTES: {
  388. upb_strview str = upb_val.str_val;
  389. ZVAL_NEW_STR(php_val, zend_string_init(str.data, str.size, 0));
  390. break;
  391. }
  392. case UPB_TYPE_MESSAGE:
  393. PBPHP_ASSERT(desc);
  394. Message_GetPhpWrapper(php_val, desc, (upb_msg*)upb_val.msg_val, arena);
  395. break;
  396. }
  397. }
  398. bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val,
  399. upb_fieldtype_t type, const Descriptor *desc,
  400. upb_arena *arena) {
  401. const upb_msgdef *subm = desc ? desc->msgdef : NULL;
  402. if (subm && upb_msgdef_iswrapper(subm) && Z_TYPE_P(val) != IS_OBJECT) {
  403. // Assigning a scalar to a wrapper-typed value. We will automatically wrap
  404. // the value, so the user doesn't need to create a FooWrapper(['value': X])
  405. // message manually.
  406. upb_msg *wrapper = upb_msg_new(subm, arena);
  407. const upb_fielddef *val_f = upb_msgdef_itof(subm, 1);
  408. upb_fieldtype_t type_f = upb_fielddef_type(val_f);
  409. upb_msgval msgval;
  410. if (!Convert_PhpToUpb(val, &msgval, type_f, NULL, arena)) return false;
  411. upb_msg_set(wrapper, val_f, msgval, arena);
  412. upb_val->msg_val = wrapper;
  413. return true;
  414. } else {
  415. // Convert_PhpToUpb doesn't auto-construct messages. This means that we only
  416. // allow:
  417. // ['foo_submsg': new Foo(['a' => 1])]
  418. // not:
  419. // ['foo_submsg': ['a' => 1]]
  420. return Convert_PhpToUpb(val, upb_val, type, desc, arena);
  421. }
  422. }
  423. void Convert_ModuleInit(void) {
  424. const char *prefix_name = "TYPE_URL_PREFIX";
  425. zend_class_entry class_type;
  426. INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil",
  427. util_methods);
  428. GPBUtil_class_entry = zend_register_internal_class(&class_type);
  429. zend_declare_class_constant_string(GPBUtil_class_entry, prefix_name,
  430. strlen(prefix_name),
  431. "type.googleapis.com/");
  432. }