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
31 #include <Zend/zend_operators.h>
32 #include <Zend/zend_exceptions.h>
33
34 #include "protobuf.h"
35 #include "utf8.h"
36
37 static zend_class_entry* util_type;
38 static const char int64_min_digits[] = "9223372036854775808";
39
40 ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1)
41 ZEND_ARG_INFO(1, val)
42 ZEND_END_ARG_INFO()
43
44 ZEND_BEGIN_ARG_INFO_EX(arg_check_message, 0, 0, 2)
45 ZEND_ARG_INFO(1, val)
46 ZEND_ARG_INFO(0, klass)
47 ZEND_END_ARG_INFO()
48
49 ZEND_BEGIN_ARG_INFO_EX(arg_check_repeated, 0, 0, 2)
50 ZEND_ARG_INFO(1, val)
51 ZEND_ARG_INFO(0, type)
52 ZEND_ARG_INFO(0, klass)
53 ZEND_END_ARG_INFO()
54
55 ZEND_BEGIN_ARG_INFO_EX(arg_check_map, 0, 0, 3)
56 ZEND_ARG_INFO(1, val)
57 ZEND_ARG_INFO(0, key_type)
58 ZEND_ARG_INFO(0, value_type)
59 ZEND_ARG_INFO(0, klass)
60 ZEND_END_ARG_INFO()
61
62 static zend_function_entry util_methods[] = {
63 PHP_ME(Util, checkInt32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
64 PHP_ME(Util, checkUint32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
65 PHP_ME(Util, checkInt64, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
66 PHP_ME(Util, checkUint64, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
67 PHP_ME(Util, checkEnum, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
68 PHP_ME(Util, checkFloat, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
69 PHP_ME(Util, checkDouble, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
70 PHP_ME(Util, checkBool, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
71 PHP_ME(Util, checkString, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
72 PHP_ME(Util, checkBytes, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
73 PHP_ME(Util, checkMessage, arg_check_message, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
74 PHP_ME(Util, checkMapField, arg_check_map, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
75 PHP_ME(Util, checkRepeatedField, arg_check_repeated,
76 ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
77 ZEND_FE_END
78 };
79
util_init(TSRMLS_D)80 void util_init(TSRMLS_D) {
81 zend_class_entry class_type;
82 INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil",
83 util_methods);
84 util_type = zend_register_internal_class(&class_type TSRMLS_CC);
85 }
86
87 // -----------------------------------------------------------------------------
88 // Type checking/conversion.
89 // -----------------------------------------------------------------------------
90
91 // This is modified from is_numeric_string in zend_operators.h. The behavior of
92 // this function is the same as is_numeric_string, except that this takes
93 // int64_t as input instead of long.
convert_numeric_string(const char * str,int length,int64_t * lval,double * dval)94 static zend_uchar convert_numeric_string(
95 const char *str, int length, int64_t *lval, double *dval) {
96 const char *ptr;
97 int base = 10, digits = 0, dp_or_e = 0;
98 double local_dval = 0.0;
99 zend_uchar type;
100
101 if (length == 0) {
102 return IS_NULL;
103 }
104
105 while (*str == ' ' || *str == '\t' || *str == '\n' ||
106 *str == '\r' || *str == '\v' || *str == '\f') {
107 str++;
108 length--;
109 }
110 ptr = str;
111
112 if (*ptr == '-' || *ptr == '+') {
113 ptr++;
114 }
115
116 if (ZEND_IS_DIGIT(*ptr)) {
117 // Handle hex numbers
118 // str is used instead of ptr to disallow signs and keep old behavior.
119 if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
120 base = 16;
121 ptr += 2;
122 }
123
124 // Skip any leading 0s.
125 while (*ptr == '0') {
126 ptr++;
127 }
128
129 // Count the number of digits. If a decimal point/exponent is found,
130 // it's a double. Otherwise, if there's a dval or no need to check for
131 // a full match, stop when there are too many digits for a int64 */
132 for (type = IS_LONG;
133 !(digits >= MAX_LENGTH_OF_INT64 && dval);
134 digits++, ptr++) {
135 check_digits:
136 if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) {
137 continue;
138 } else if (base == 10) {
139 if (*ptr == '.' && dp_or_e < 1) {
140 goto process_double;
141 } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) {
142 const char *e = ptr + 1;
143
144 if (*e == '-' || *e == '+') {
145 ptr = e++;
146 }
147 if (ZEND_IS_DIGIT(*e)) {
148 goto process_double;
149 }
150 }
151 }
152 break;
153 }
154
155 if (base == 10) {
156 if (digits >= MAX_LENGTH_OF_INT64) {
157 dp_or_e = -1;
158 goto process_double;
159 }
160 } else if (!(digits < SIZEOF_INT64 * 2 ||
161 (digits == SIZEOF_INT64 * 2 && ptr[-digits] <= '7'))) {
162 if (dval) {
163 local_dval = zend_hex_strtod(str, &ptr);
164 }
165 type = IS_DOUBLE;
166 }
167 } else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) {
168 process_double:
169 type = IS_DOUBLE;
170
171 // If there's a dval, do the conversion; else continue checking
172 // the digits if we need to check for a full match.
173 if (dval) {
174 local_dval = zend_strtod(str, &ptr);
175 } else if (dp_or_e != -1) {
176 dp_or_e = (*ptr++ == '.') ? 1 : 2;
177 goto check_digits;
178 }
179 } else {
180 return IS_NULL;
181 }
182 if (ptr != str + length) {
183 zend_error(E_NOTICE, "A non well formed numeric value encountered");
184 return 0;
185 }
186
187 if (type == IS_LONG) {
188 if (digits == MAX_LENGTH_OF_INT64 - 1) {
189 int cmp = strcmp(&ptr[-digits], int64_min_digits);
190
191 if (!(cmp < 0 || (cmp == 0 && *str == '-'))) {
192 if (dval) {
193 *dval = zend_strtod(str, NULL);
194 }
195
196 return IS_DOUBLE;
197 }
198 }
199 if (lval) {
200 *lval = strtoll(str, NULL, base);
201 }
202 return IS_LONG;
203 } else {
204 if (dval) {
205 *dval = local_dval;
206 }
207 return IS_DOUBLE;
208 }
209 }
210
211 #define CONVERT_TO_INTEGER(type) \
212 static bool convert_int64_to_##type(int64_t val, type##_t* type##_value) { \
213 *type##_value = (type##_t)val; \
214 return true; \
215 } \
216 \
217 static bool convert_double_to_##type(double val, type##_t* type##_value) { \
218 *type##_value = (type##_t)zend_dval_to_lval(val); \
219 return true; \
220 } \
221 \
222 static bool convert_string_to_##type(const char* val, int len, \
223 type##_t* type##_value) { \
224 int64_t lval; \
225 double dval; \
226 TSRMLS_FETCH(); \
227 switch (convert_numeric_string(val, len, &lval, &dval)) { \
228 case IS_DOUBLE: { \
229 return convert_double_to_##type(dval, type##_value); \
230 } \
231 case IS_LONG: { \
232 return convert_int64_to_##type(lval, type##_value); \
233 } \
234 default: \
235 zend_throw_exception(NULL, \
236 "Given string value cannot be converted to integer.", \
237 0 TSRMLS_CC); \
238 return false; \
239 } \
240 } \
241 \
242 bool protobuf_convert_to_##type(zval* from, type##_t* to) { \
243 TSRMLS_FETCH(); \
244 switch (Z_TYPE_P(from)) { \
245 case IS_LONG: { \
246 return convert_int64_to_##type(Z_LVAL_P(from), to); \
247 } \
248 case IS_DOUBLE: { \
249 return convert_double_to_##type(Z_DVAL_P(from), to); \
250 } \
251 case IS_STRING: { \
252 return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from), \
253 to); \
254 } \
255 default: { \
256 zend_throw_exception(NULL, \
257 "Given value cannot be converted to integer.", \
258 0 TSRMLS_CC); \
259 return false; \
260 } \
261 } \
262 return false; \
263 }
264
265 CONVERT_TO_INTEGER(int32);
266 CONVERT_TO_INTEGER(uint32);
267 CONVERT_TO_INTEGER(int64);
268 CONVERT_TO_INTEGER(uint64);
269
270 #undef CONVERT_TO_INTEGER
271
272 #define CONVERT_TO_FLOAT(type) \
273 static bool convert_int64_to_##type(int64_t val, type* type##_value) { \
274 *type##_value = (type)val; \
275 return true; \
276 } \
277 \
278 static bool convert_double_to_##type(double val, type* type##_value) { \
279 *type##_value = (type)val; \
280 return true; \
281 } \
282 \
283 static bool convert_string_to_##type(const char* val, int len, \
284 type* type##_value) { \
285 int64_t lval; \
286 double dval; \
287 \
288 TSRMLS_FETCH(); \
289 switch (convert_numeric_string(val, len, &lval, &dval)) { \
290 case IS_DOUBLE: { \
291 *type##_value = (type)dval; \
292 return true; \
293 } \
294 case IS_LONG: { \
295 *type##_value = (type)lval; \
296 return true; \
297 } \
298 default: \
299 zend_throw_exception(NULL, \
300 "Given string value cannot be converted to integer.", \
301 0 TSRMLS_CC); \
302 return false; \
303 } \
304 } \
305 \
306 bool protobuf_convert_to_##type(zval* from, type* to) { \
307 TSRMLS_FETCH(); \
308 switch (Z_TYPE_P(from)) { \
309 case IS_LONG: { \
310 return convert_int64_to_##type(Z_LVAL_P(from), to); \
311 } \
312 case IS_DOUBLE: { \
313 return convert_double_to_##type(Z_DVAL_P(from), to); \
314 } \
315 case IS_STRING: { \
316 return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from), \
317 to); \
318 } \
319 default: { \
320 zend_throw_exception(NULL, \
321 "Given value cannot be converted to integer.", \
322 0 TSRMLS_CC); \
323 return false; \
324 } \
325 } \
326 return false; \
327 }
328
329 CONVERT_TO_FLOAT(float);
330 CONVERT_TO_FLOAT(double);
331
332 #undef CONVERT_TO_FLOAT
333
protobuf_convert_to_bool(zval * from,int8_t * to)334 bool protobuf_convert_to_bool(zval* from, int8_t* to) {
335 TSRMLS_FETCH();
336 switch (Z_TYPE_P(from)) {
337 #if PHP_MAJOR_VERSION < 7
338 case IS_BOOL:
339 *to = (int8_t)Z_BVAL_P(from);
340 break;
341 #else
342 case IS_TRUE:
343 *to = 1;
344 break;
345 case IS_FALSE:
346 *to = 0;
347 break;
348 #endif
349 case IS_LONG:
350 *to = (int8_t)(Z_LVAL_P(from) != 0);
351 break;
352 case IS_DOUBLE:
353 *to = (int8_t)(Z_LVAL_P(from) != 0);
354 break;
355 case IS_STRING: {
356 char* strval = Z_STRVAL_P(from);
357
358 if (Z_STRLEN_P(from) == 0 ||
359 (Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) {
360 *to = 0;
361 } else {
362 *to = 1;
363 }
364 } break;
365 default: {
366 zend_throw_exception(
367 NULL, "Given value cannot be converted to bool.",
368 0 TSRMLS_CC);
369 return false;
370 }
371 }
372 return true;
373 }
374
protobuf_convert_to_string(zval * from)375 bool protobuf_convert_to_string(zval* from) {
376 #if PHP_MAJOR_VERSION >= 7
377 if (Z_ISREF_P(from)) {
378 ZVAL_DEREF(from);
379 }
380 #endif
381 TSRMLS_FETCH();
382 switch (Z_TYPE_P(from)) {
383 case IS_STRING: {
384 return true;
385 }
386 #if PHP_MAJOR_VERSION < 7
387 case IS_BOOL:
388 #else
389 case IS_TRUE:
390 case IS_FALSE:
391 #endif
392 case IS_LONG:
393 case IS_DOUBLE: {
394 zval tmp;
395 php_proto_zend_make_printable_zval(from, &tmp);
396 ZVAL_COPY_VALUE(from, &tmp);
397 return true;
398 }
399 default:
400 zend_throw_exception(
401 NULL, "Given value cannot be converted to string.",
402 0 TSRMLS_CC);
403 return false;
404 }
405 }
406
407 // -----------------------------------------------------------------------------
408 // PHP Functions.
409 // -----------------------------------------------------------------------------
410
411 // The implementation of type checking for primitive fields is empty. This is
412 // because type checking is done when direct assigning message fields (e.g.,
413 // foo->a = 1). Functions defined here are place holders in generated code for
414 // pure PHP implementation (c extension and pure PHP share the same generated
415 // code).
416 #define PHP_TYPE_CHECK(type) \
417 PHP_METHOD(Util, check##type) {}
418
419 PHP_TYPE_CHECK(Int32)
PHP_TYPE_CHECK(Uint32)420 PHP_TYPE_CHECK(Uint32)
421 PHP_TYPE_CHECK(Int64)
422 PHP_TYPE_CHECK(Uint64)
423 PHP_TYPE_CHECK(Enum)
424 PHP_TYPE_CHECK(Float)
425 PHP_TYPE_CHECK(Double)
426 PHP_TYPE_CHECK(Bool)
427 PHP_TYPE_CHECK(String)
428 PHP_TYPE_CHECK(Bytes)
429
430 #undef PHP_TYPE_CHECK
431
432 PHP_METHOD(Util, checkMessage) {
433 zval* val;
434 zend_class_entry* klass = NULL;
435 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!C", &val, &klass) ==
436 FAILURE) {
437 return;
438 }
439 if (val == NULL) {
440 RETURN_NULL();
441 }
442 if (!instanceof_function(Z_OBJCE_P(val), klass TSRMLS_CC)) {
443 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
444 "Given value is not an instance of %s.",
445 klass->name);
446 return;
447 }
448 RETURN_ZVAL(val, 1, 0);
449 }
450
check_repeated_field(const zend_class_entry * klass,PHP_PROTO_LONG type,zval * val,zval * return_value)451 void check_repeated_field(const zend_class_entry* klass, PHP_PROTO_LONG type,
452 zval* val, zval* return_value) {
453 #if PHP_MAJOR_VERSION >= 7
454 if (Z_ISREF_P(val)) {
455 ZVAL_DEREF(val);
456 }
457 #endif
458
459 TSRMLS_FETCH();
460 if (Z_TYPE_P(val) == IS_ARRAY) {
461 HashTable* table = HASH_OF(val);
462 HashPosition pointer;
463 void* memory;
464
465 #if PHP_MAJOR_VERSION < 7
466 zval* repeated_field;
467 MAKE_STD_ZVAL(repeated_field);
468 #else
469 zval repeated_field;
470 #endif
471
472 repeated_field_create_with_type(repeated_field_type, to_fieldtype(type),
473 klass, &repeated_field TSRMLS_CC);
474
475 for (zend_hash_internal_pointer_reset_ex(table, &pointer);
476 php_proto_zend_hash_get_current_data_ex(table, (void**)&memory,
477 &pointer) == SUCCESS;
478 zend_hash_move_forward_ex(table, &pointer)) {
479 repeated_field_handlers->write_dimension(
480 CACHED_TO_ZVAL_PTR(repeated_field), NULL,
481 CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory) TSRMLS_CC);
482 }
483
484 RETURN_ZVAL(CACHED_TO_ZVAL_PTR(repeated_field), 1, 1);
485
486 } else if (Z_TYPE_P(val) == IS_OBJECT) {
487 if (!instanceof_function(Z_OBJCE_P(val), repeated_field_type TSRMLS_CC)) {
488 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
489 "Given value is not an instance of %s.",
490 repeated_field_type->name);
491 return;
492 }
493 RepeatedField* intern = UNBOX(RepeatedField, val);
494 if (to_fieldtype(type) != intern->type) {
495 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
496 "Incorrect repeated field type.");
497 return;
498 }
499 if (klass != NULL && intern->msg_ce != klass) {
500 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
501 "Expect a repeated field of %s, but %s is given.",
502 klass->name, intern->msg_ce->name);
503 return;
504 }
505 RETURN_ZVAL(val, 1, 0);
506 } else {
507 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
508 "Incorrect repeated field type.");
509 return;
510 }
511 }
512
PHP_METHOD(Util,checkRepeatedField)513 PHP_METHOD(Util, checkRepeatedField) {
514 zval* val;
515 PHP_PROTO_LONG type;
516 const zend_class_entry* klass = NULL;
517 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zl|C", &val, &type,
518 &klass) == FAILURE) {
519 return;
520 }
521 RETURN_ZVAL(val, 1, 0);
522 }
523
check_map_field(const zend_class_entry * klass,PHP_PROTO_LONG key_type,PHP_PROTO_LONG value_type,zval * val,zval * return_value)524 void check_map_field(const zend_class_entry* klass, PHP_PROTO_LONG key_type,
525 PHP_PROTO_LONG value_type, zval* val, zval* return_value) {
526 #if PHP_MAJOR_VERSION >= 7
527 if (Z_ISREF_P(val)) {
528 ZVAL_DEREF(val);
529 }
530 #endif
531
532 TSRMLS_FETCH();
533 if (Z_TYPE_P(val) == IS_ARRAY) {
534 HashTable* table = Z_ARRVAL_P(val);
535 HashPosition pointer;
536 zval key;
537 void* value;
538
539 #if PHP_MAJOR_VERSION < 7
540 zval* map_field;
541 MAKE_STD_ZVAL(map_field);
542 #else
543 zval map_field;
544 #endif
545
546 map_field_create_with_type(map_field_type, to_fieldtype(key_type),
547 to_fieldtype(value_type), klass,
548 &map_field TSRMLS_CC);
549
550 for (zend_hash_internal_pointer_reset_ex(table, &pointer);
551 php_proto_zend_hash_get_current_data_ex(table, (void**)&value,
552 &pointer) == SUCCESS;
553 zend_hash_move_forward_ex(table, &pointer)) {
554 zend_hash_get_current_key_zval_ex(table, &key, &pointer);
555 map_field_handlers->write_dimension(
556 CACHED_TO_ZVAL_PTR(map_field), &key,
557 CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)value) TSRMLS_CC);
558 zval_dtor(&key);
559 }
560
561 RETURN_ZVAL(CACHED_TO_ZVAL_PTR(map_field), 1, 1);
562 } else if (Z_TYPE_P(val) == IS_OBJECT) {
563 if (!instanceof_function(Z_OBJCE_P(val), map_field_type TSRMLS_CC)) {
564 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
565 "Given value is not an instance of %s.",
566 map_field_type->name);
567 return;
568 }
569 Map* intern = UNBOX(Map, val);
570 if (to_fieldtype(key_type) != intern->key_type) {
571 zend_throw_exception(
572 NULL, "Incorrect map field key type.",
573 0 TSRMLS_CC);
574 return;
575 }
576 if (to_fieldtype(value_type) != intern->value_type) {
577 zend_throw_exception(
578 NULL, "Incorrect map field value type.",
579 0 TSRMLS_CC);
580 return;
581 }
582 if (klass != NULL && intern->msg_ce != klass) {
583 zend_throw_exception_ex(NULL, 0 TSRMLS_CC,
584 "Expect a map field of %s, but %s is given.",
585 klass->name, intern->msg_ce->name);
586 return;
587 }
588 RETURN_ZVAL(val, 1, 0);
589 } else {
590 zend_throw_exception(
591 NULL, "Incorrect map field type.",
592 0 TSRMLS_CC);
593 return;
594 }
595 }
596
PHP_METHOD(Util,checkMapField)597 PHP_METHOD(Util, checkMapField) {
598 zval* val;
599 PHP_PROTO_LONG key_type, value_type;
600 const zend_class_entry* klass = NULL;
601 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zll|C", &val, &key_type,
602 &value_type, &klass) == FAILURE) {
603 return;
604 }
605 RETURN_ZVAL(val, 1, 0);
606 }
607