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 <ext/spl/spl_iterators.h>
32 #include <Zend/zend_API.h>
33 #include <Zend/zend_interfaces.h>
34
35 #include "protobuf.h"
36 #include "utf8.h"
37
38 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
39 ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()40 ZEND_END_ARG_INFO()
41
42 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
43 ZEND_ARG_INFO(0, index)
44 ZEND_ARG_INFO(0, newval)
45 ZEND_END_ARG_INFO()
46
47 ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
48 ZEND_END_ARG_INFO()
49
50 // Utilities
51
52 void* upb_value_memory(upb_value* v) {
53 return (void*)(&v->val);
54 }
55
56 // -----------------------------------------------------------------------------
57 // Basic map operations on top of upb's strtable.
58 //
59 // Note that we roll our own `Map` container here because, as for
60 // `RepeatedField`, we want a strongly-typed container. This is so that any user
61 // errors due to incorrect map key or value types are raised as close as
62 // possible to the error site, rather than at some deferred point (e.g.,
63 // serialization).
64 //
65 // We build our `Map` on top of upb_strtable so that we're able to take
66 // advantage of the native_slot storage abstraction, as RepeatedField does.
67 // (This is not quite a perfect mapping -- see the key conversions below -- but
68 // gives us full support and error-checking for all value types for free.)
69 // -----------------------------------------------------------------------------
70
71 // Map values are stored using the native_slot abstraction (as with repeated
72 // field values), but keys are a bit special. Since we use a strtable, we need
73 // to store keys as sequences of bytes such that equality of those bytes maps
74 // one-to-one to equality of keys. We store strings directly (i.e., they map to
75 // their own bytes) and integers as native integers (using the native_slot
76 // abstraction).
77
78 // Note that there is another tradeoff here in keeping string keys as native
79 // strings rather than PHP strings: traversing the Map requires conversion to
80 // PHP string values on every traversal, potentially creating more garbage. We
81 // should consider ways to cache a PHP version of the key if this becomes an
82 // issue later.
83
84 // Forms a key to use with the underlying strtable from a PHP key value. |buf|
85 // must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to
86 // construct a key byte sequence if needed. |out_key| and |out_length| provide
87 // the resulting key data/length.
88 #define TABLE_KEY_BUF_LENGTH 8 // sizeof(uint64_t)
table_key(Map * self,zval * key,char * buf,const char ** out_key,size_t * out_length TSRMLS_DC)89 static bool table_key(Map* self, zval* key,
90 char* buf,
91 const char** out_key,
92 size_t* out_length TSRMLS_DC) {
93 switch (self->key_type) {
94 case UPB_TYPE_STRING:
95 if (!protobuf_convert_to_string(key)) {
96 return false;
97 }
98 if (!is_structurally_valid_utf8(Z_STRVAL_P(key), Z_STRLEN_P(key))) {
99 zend_error(E_USER_ERROR, "Given key is not UTF8 encoded.");
100 return false;
101 }
102 *out_key = Z_STRVAL_P(key);
103 *out_length = Z_STRLEN_P(key);
104 break;
105
106 #define CASE_TYPE(upb_type, type, c_type, php_type) \
107 case UPB_TYPE_##upb_type: { \
108 c_type type##_value; \
109 if (!protobuf_convert_to_##type(key, &type##_value)) { \
110 return false; \
111 } \
112 native_slot_set_by_array(self->key_type, NULL, buf, key TSRMLS_CC); \
113 *out_key = buf; \
114 *out_length = native_slot_size(self->key_type); \
115 break; \
116 }
117 CASE_TYPE(BOOL, bool, int8_t, BOOL)
118 CASE_TYPE(INT32, int32, int32_t, LONG)
119 CASE_TYPE(INT64, int64, int64_t, LONG)
120 CASE_TYPE(UINT32, uint32, uint32_t, LONG)
121 CASE_TYPE(UINT64, uint64, uint64_t, LONG)
122
123 #undef CASE_TYPE
124
125 default:
126 // Map constructor should not allow a Map with another key type to be
127 // constructed.
128 assert(false);
129 break;
130 }
131
132 return true;
133 }
134
135 // -----------------------------------------------------------------------------
136 // MapField methods
137 // -----------------------------------------------------------------------------
138
139 static zend_function_entry map_field_methods[] = {
140 PHP_ME(MapField, __construct, NULL, ZEND_ACC_PUBLIC)
141 PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
142 PHP_ME(MapField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC)
143 PHP_ME(MapField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC)
144 PHP_ME(MapField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC)
145 PHP_ME(MapField, count, arginfo_void, ZEND_ACC_PUBLIC)
146 PHP_ME(MapField, getIterator, arginfo_void, ZEND_ACC_PUBLIC)
147 ZEND_FE_END
148 };
149
150 // Forward declare static functions.
151
152 static void map_field_write_dimension(zval *object, zval *key,
153 zval *value TSRMLS_DC);
154
155 // -----------------------------------------------------------------------------
156 // MapField creation/desctruction
157 // -----------------------------------------------------------------------------
158
159 zend_class_entry* map_field_type;
160 zend_class_entry* map_field_iter_type;
161
162 zend_object_handlers* map_field_handlers;
163 zend_object_handlers* map_field_iter_handlers;
164
map_begin_internal(Map * map,MapIter * iter)165 static void map_begin_internal(Map *map, MapIter *iter) {
166 iter->self = map;
167 upb_strtable_begin(&iter->it, &map->table);
168 }
169
map_field_get_gc(zval * object,CACHED_VALUE ** table,int * n TSRMLS_DC)170 static HashTable *map_field_get_gc(zval *object, CACHED_VALUE **table,
171 int *n TSRMLS_DC) {
172 // TODO(teboring): Unfortunately, zend engine does not support garbage
173 // collection for custom array. We have to use zend engine's native array
174 // instead.
175 *table = NULL;
176 *n = 0;
177 return NULL;
178 }
179
180 // Define map value element free function.
181 #if PHP_MAJOR_VERSION < 7
php_proto_map_string_release(void * value)182 static inline void php_proto_map_string_release(void *value) {
183 zval_ptr_dtor(value);
184 }
185
php_proto_map_object_release(void * value)186 static inline void php_proto_map_object_release(void *value) {
187 zval_ptr_dtor(value);
188 }
189 #else
php_proto_map_string_release(void * value)190 static inline void php_proto_map_string_release(void *value) {
191 zend_string* object = *(zend_string**)value;
192 zend_string_release(object);
193 }
php_proto_map_object_release(void * value)194 static inline void php_proto_map_object_release(void *value) {
195 zend_object* object = *(zend_object**)value;
196 GC_DELREF(object);
197 if(GC_REFCOUNT(object) == 0) {
198 zend_objects_store_del(object);
199 }
200 }
201 #endif
202
203 // Define object free method.
PHP_PROTO_OBJECT_FREE_START(Map,map_field)204 PHP_PROTO_OBJECT_FREE_START(Map, map_field)
205 MapIter it;
206 int len;
207 for (map_begin_internal(intern, &it); !map_done(&it); map_next(&it)) {
208 upb_value value = map_iter_value(&it, &len);
209 void *mem = upb_value_memory(&value);
210 switch (intern->value_type) {
211 case UPB_TYPE_MESSAGE:
212 php_proto_map_object_release(mem);
213 break;
214 case UPB_TYPE_STRING:
215 case UPB_TYPE_BYTES:
216 php_proto_map_string_release(mem);
217 break;
218 default:
219 break;
220 }
221 }
222 upb_strtable_uninit(&intern->table);
223 PHP_PROTO_OBJECT_FREE_END
224
225 PHP_PROTO_OBJECT_DTOR_START(Map, map_field)
226 PHP_PROTO_OBJECT_DTOR_END
227
228 // Define object create method.
229 PHP_PROTO_OBJECT_CREATE_START(Map, map_field)
230 // Table value type is always UINT64: this ensures enough space to store the
231 // native_slot value.
232 if (!upb_strtable_init(&intern->table, UPB_CTYPE_UINT64)) {
233 zend_error(E_USER_ERROR, "Could not allocate table.");
234 }
235 PHP_PROTO_OBJECT_CREATE_END(Map, map_field)
236
237 // Init class entry.
238 PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapField", Map,
239 map_field)
240 zend_class_implements(map_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess,
241 zend_ce_aggregate, spl_ce_Countable);
242 map_field_handlers->write_dimension = map_field_write_dimension;
243 map_field_handlers->get_gc = map_field_get_gc;
244 PHP_PROTO_INIT_CLASS_END
245
map_field_create_with_field(const zend_class_entry * ce,const upb_fielddef * field,CACHED_VALUE * map_field PHP_PROTO_TSRMLS_DC)246 void map_field_create_with_field(const zend_class_entry *ce,
247 const upb_fielddef *field,
248 CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
249 const upb_fielddef *key_field = map_field_key(field);
250 const upb_fielddef *value_field = map_field_value(field);
251 map_field_create_with_type(
252 ce, upb_fielddef_type(key_field), upb_fielddef_type(value_field),
253 field_type_class(value_field TSRMLS_CC), map_field PHP_PROTO_TSRMLS_CC);
254 }
255
map_field_create_with_type(const zend_class_entry * ce,upb_fieldtype_t key_type,upb_fieldtype_t value_type,const zend_class_entry * msg_ce,CACHED_VALUE * map_field PHP_PROTO_TSRMLS_DC)256 void map_field_create_with_type(const zend_class_entry *ce,
257 upb_fieldtype_t key_type,
258 upb_fieldtype_t value_type,
259 const zend_class_entry *msg_ce,
260 CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
261 CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(CACHED_PTR_TO_ZVAL_PTR(map_field),
262 map_field_type);
263 Map *intern = UNBOX(Map, CACHED_TO_ZVAL_PTR(*map_field));
264 intern->key_type = key_type;
265 intern->value_type = value_type;
266 intern->msg_ce = msg_ce;
267 }
268
269 // -----------------------------------------------------------------------------
270 // MapField Handlers
271 // -----------------------------------------------------------------------------
272
map_field_read_dimension(zval * object,zval * key,int type,CACHED_VALUE * retval TSRMLS_DC)273 static bool map_field_read_dimension(zval *object, zval *key, int type,
274 CACHED_VALUE *retval TSRMLS_DC) {
275 Map *intern = UNBOX(Map, object);
276
277 char keybuf[TABLE_KEY_BUF_LENGTH];
278 const char* keyval = NULL;
279 size_t length = 0;
280 upb_value v;
281 #ifndef NDEBUG
282 v.ctype = UPB_CTYPE_UINT64;
283 #endif
284 if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
285 return false;
286 }
287
288 if (upb_strtable_lookup2(&intern->table, keyval, length, &v)) {
289 void* mem = upb_value_memory(&v);
290 native_slot_get_by_map_value(intern->value_type, mem, retval TSRMLS_CC);
291 return true;
292 } else {
293 zend_error(E_USER_ERROR, "Given key doesn't exist.");
294 return false;
295 }
296 }
297
map_index_unset(Map * intern,const char * keyval,int length)298 static void map_index_unset(Map *intern, const char* keyval, int length) {
299 upb_value old_value;
300 if (upb_strtable_remove2(&intern->table, keyval, length, &old_value)) {
301 switch (intern->value_type) {
302 case UPB_TYPE_MESSAGE: {
303 #if PHP_MAJOR_VERSION < 7
304 zval_ptr_dtor(upb_value_memory(&old_value));
305 #else
306 zend_object* object = *(zend_object**)upb_value_memory(&old_value);
307 GC_DELREF(object);
308 if(GC_REFCOUNT(object) == 0) {
309 zend_objects_store_del(object);
310 }
311 #endif
312 break;
313 }
314 case UPB_TYPE_STRING:
315 case UPB_TYPE_BYTES: {
316 #if PHP_MAJOR_VERSION < 7
317 zval_ptr_dtor(upb_value_memory(&old_value));
318 #else
319 zend_string* object = *(zend_string**)upb_value_memory(&old_value);
320 zend_string_release(object);
321 #endif
322 break;
323 }
324 default:
325 break;
326 }
327 }
328 }
329
map_index_set(Map * intern,const char * keyval,int length,upb_value v)330 bool map_index_set(Map *intern, const char* keyval, int length, upb_value v) {
331 // Replace any existing value by issuing a 'remove' operation first.
332 map_index_unset(intern, keyval, length);
333
334 if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
335 zend_error(E_USER_ERROR, "Could not insert into table");
336 return false;
337 }
338
339 return true;
340 }
341
map_field_write_dimension(zval * object,zval * key,zval * value TSRMLS_DC)342 static void map_field_write_dimension(zval *object, zval *key,
343 zval *value TSRMLS_DC) {
344 Map *intern = UNBOX(Map, object);
345
346 char keybuf[TABLE_KEY_BUF_LENGTH];
347 const char* keyval = NULL;
348 size_t length = 0;
349 upb_value v;
350 void* mem;
351 if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
352 return;
353 }
354
355 mem = upb_value_memory(&v);
356 memset(mem, 0, native_slot_size(intern->value_type));
357 if (!native_slot_set_by_map(intern->value_type, intern->msg_ce, mem,
358 value TSRMLS_CC)) {
359 return;
360 }
361 #ifndef NDEBUG
362 v.ctype = UPB_CTYPE_UINT64;
363 #endif
364
365 map_index_set(intern, keyval, length, v);
366 }
367
map_field_unset_dimension(zval * object,zval * key TSRMLS_DC)368 static bool map_field_unset_dimension(zval *object, zval *key TSRMLS_DC) {
369 Map *intern = UNBOX(Map, object);
370
371 char keybuf[TABLE_KEY_BUF_LENGTH];
372 const char* keyval = NULL;
373 size_t length = 0;
374 upb_value v;
375 if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
376 return false;
377 }
378 #ifndef NDEBUG
379 v.ctype = UPB_CTYPE_UINT64;
380 #endif
381
382 map_index_unset(intern, keyval, length);
383
384 return true;
385 }
386
387 // -----------------------------------------------------------------------------
388 // PHP MapField Methods
389 // -----------------------------------------------------------------------------
390
PHP_METHOD(MapField,__construct)391 PHP_METHOD(MapField, __construct) {
392 long key_type, value_type;
393 zend_class_entry* klass = NULL;
394
395 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|C", &key_type,
396 &value_type, &klass) == FAILURE) {
397 return;
398 }
399
400 Map *intern = UNBOX(Map, getThis());
401 intern->key_type = to_fieldtype(key_type);
402 intern->value_type = to_fieldtype(value_type);
403 intern->msg_ce = klass;
404
405 // Check that the key type is an allowed type.
406 switch (intern->key_type) {
407 case UPB_TYPE_INT32:
408 case UPB_TYPE_INT64:
409 case UPB_TYPE_UINT32:
410 case UPB_TYPE_UINT64:
411 case UPB_TYPE_BOOL:
412 case UPB_TYPE_STRING:
413 case UPB_TYPE_BYTES:
414 // These are OK.
415 break;
416 default:
417 zend_error(E_USER_ERROR, "Invalid key type for map.");
418 }
419 }
420
PHP_METHOD(MapField,offsetExists)421 PHP_METHOD(MapField, offsetExists) {
422 zval *key;
423 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) ==
424 FAILURE) {
425 return;
426 }
427
428 Map *intern = UNBOX(Map, getThis());
429
430 char keybuf[TABLE_KEY_BUF_LENGTH];
431 const char* keyval = NULL;
432 size_t length = 0;
433 upb_value v;
434 #ifndef NDEBUG
435 v.ctype = UPB_CTYPE_UINT64;
436 #endif
437 if (!table_key(intern, key, keybuf, &keyval, &length TSRMLS_CC)) {
438 RETURN_BOOL(false);
439 }
440
441 RETURN_BOOL(upb_strtable_lookup2(&intern->table, keyval, length, &v));
442 }
443
PHP_METHOD(MapField,offsetGet)444 PHP_METHOD(MapField, offsetGet) {
445 zval *index, *value;
446 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
447 FAILURE) {
448 return;
449 }
450 map_field_read_dimension(getThis(), index, BP_VAR_R,
451 ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
452 }
453
PHP_METHOD(MapField,offsetSet)454 PHP_METHOD(MapField, offsetSet) {
455 zval *index, *value;
456 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
457 FAILURE) {
458 return;
459 }
460 map_field_write_dimension(getThis(), index, value TSRMLS_CC);
461 }
462
PHP_METHOD(MapField,offsetUnset)463 PHP_METHOD(MapField, offsetUnset) {
464 zval *index;
465 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
466 FAILURE) {
467 return;
468 }
469 map_field_unset_dimension(getThis(), index TSRMLS_CC);
470 }
471
PHP_METHOD(MapField,count)472 PHP_METHOD(MapField, count) {
473 Map *intern = UNBOX(Map, getThis());
474
475 if (zend_parse_parameters_none() == FAILURE) {
476 return;
477 }
478
479 RETURN_LONG(upb_strtable_count(&intern->table));
480 }
481
PHP_METHOD(MapField,getIterator)482 PHP_METHOD(MapField, getIterator) {
483 CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(return_value,
484 map_field_iter_type);
485
486 Map *intern = UNBOX(Map, getThis());
487 MapIter *iter = UNBOX(MapIter, return_value);
488 map_begin(getThis(), iter TSRMLS_CC);
489 }
490
491 // -----------------------------------------------------------------------------
492 // Map Iterator
493 // -----------------------------------------------------------------------------
494
map_begin(zval * map_php,MapIter * iter TSRMLS_DC)495 void map_begin(zval *map_php, MapIter *iter TSRMLS_DC) {
496 Map *self = UNBOX(Map, map_php);
497 map_begin_internal(self, iter);
498 }
499
map_next(MapIter * iter)500 void map_next(MapIter *iter) {
501 upb_strtable_next(&iter->it);
502 }
503
map_done(MapIter * iter)504 bool map_done(MapIter *iter) {
505 return upb_strtable_done(&iter->it);
506 }
507
map_iter_key(MapIter * iter,int * len)508 const char *map_iter_key(MapIter *iter, int *len) {
509 *len = upb_strtable_iter_keylength(&iter->it);
510 return upb_strtable_iter_key(&iter->it);
511 }
512
map_iter_value(MapIter * iter,int * len)513 upb_value map_iter_value(MapIter *iter, int *len) {
514 *len = native_slot_size(iter->self->value_type);
515 return upb_strtable_iter_value(&iter->it);
516 }
517
518 // -----------------------------------------------------------------------------
519 // MapFieldIter methods
520 // -----------------------------------------------------------------------------
521 static zend_function_entry map_field_iter_methods[] = {
522 PHP_ME(MapFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC)
523 PHP_ME(MapFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC)
524 PHP_ME(MapFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC)
525 PHP_ME(MapFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC)
526 PHP_ME(MapFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC)
527 ZEND_FE_END
528 };
529
530 // -----------------------------------------------------------------------------
531 // MapFieldIter creation/desctruction
532 // -----------------------------------------------------------------------------
533
534 // Define object free method.
535 PHP_PROTO_OBJECT_FREE_START(MapIter, map_field_iter)
536 PHP_PROTO_OBJECT_FREE_END
537
538 PHP_PROTO_OBJECT_DTOR_START(MapIter, map_field_iter)
539 PHP_PROTO_OBJECT_DTOR_END
540
541 // Define object create method.
542 PHP_PROTO_OBJECT_CREATE_START(MapIter, map_field_iter)
543 intern->self = NULL;
544 PHP_PROTO_OBJECT_CREATE_END(MapIter, map_field_iter)
545
546 // Init class entry.
547 PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapFieldIter",
548 MapIter, map_field_iter)
549 zend_class_implements(map_field_iter_type TSRMLS_CC, 1, zend_ce_iterator);
550 PHP_PROTO_INIT_CLASS_END
551
552 // -----------------------------------------------------------------------------
553 // PHP MapFieldIter Methods
554 // -----------------------------------------------------------------------------
555
PHP_METHOD(MapFieldIter,rewind)556 PHP_METHOD(MapFieldIter, rewind) {
557 MapIter *intern = UNBOX(MapIter, getThis());
558 map_begin_internal(intern->self, intern);
559 }
560
PHP_METHOD(MapFieldIter,current)561 PHP_METHOD(MapFieldIter, current) {
562 MapIter *intern = UNBOX(MapIter, getThis());
563 Map *map_field = intern->self;
564
565 int value_length = 0;
566 upb_value value = map_iter_value(intern, &value_length);
567
568 void* mem = upb_value_memory(&value);
569 native_slot_get_by_map_value(map_field->value_type, mem,
570 ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
571 }
572
PHP_METHOD(MapFieldIter,key)573 PHP_METHOD(MapFieldIter, key) {
574 MapIter *intern = UNBOX(MapIter, getThis());
575 Map *map_field = intern->self;
576
577 int key_length = 0;
578 const char* key = map_iter_key(intern, &key_length);
579
580 native_slot_get_by_map_key(map_field->key_type, key, key_length,
581 ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
582 }
583
PHP_METHOD(MapFieldIter,next)584 PHP_METHOD(MapFieldIter, next) {
585 MapIter *intern = UNBOX(MapIter, getThis());
586 map_next(intern);
587 }
588
PHP_METHOD(MapFieldIter,valid)589 PHP_METHOD(MapFieldIter, valid) {
590 MapIter *intern = UNBOX(MapIter, getThis());
591 RETURN_BOOL(!map_done(intern));
592 }
593