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