• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "map.h"
32 
33 #include <Zend/zend_API.h>
34 #include <Zend/zend_interfaces.h>
35 
36 #include <ext/spl/spl_iterators.h>
37 
38 #include "arena.h"
39 #include "convert.h"
40 #include "message.h"
41 #include "php-upb.h"
42 #include "protobuf.h"
43 
44 static void MapFieldIter_make(zval *val, zval *map_field);
45 
46 // -----------------------------------------------------------------------------
47 // MapField
48 // -----------------------------------------------------------------------------
49 
50 typedef struct {
51   zend_object std;
52   zval arena;
53   upb_Map *map;
54   MapField_Type type;
55 } MapField;
56 
57 zend_class_entry *MapField_class_entry;
58 static zend_object_handlers MapField_object_handlers;
59 
MapType_Eq(MapField_Type a,MapField_Type b)60 static bool MapType_Eq(MapField_Type a, MapField_Type b) {
61   return a.key_type == b.key_type && TypeInfo_Eq(a.val_type, b.val_type);
62 }
63 
KeyType(MapField_Type type)64 static TypeInfo KeyType(MapField_Type type) {
65   TypeInfo ret = {type.key_type};
66   return ret;
67 }
68 
MapType_Get(const upb_FieldDef * f)69 MapField_Type MapType_Get(const upb_FieldDef *f) {
70   const upb_MessageDef *ent = upb_FieldDef_MessageSubDef(f);
71   const upb_FieldDef *key_f = upb_MessageDef_FindFieldByNumber(ent, 1);
72   const upb_FieldDef *val_f = upb_MessageDef_FindFieldByNumber(ent, 2);
73   MapField_Type type = {
74       upb_FieldDef_CType(key_f),
75       {upb_FieldDef_CType(val_f), Descriptor_GetFromFieldDef(val_f)}};
76   return type;
77 }
78 
79 // PHP Object Handlers /////////////////////////////////////////////////////////
80 
81 /**
82  * MapField_create()
83  *
84  * PHP class entry function to allocate and initialize a new MapField
85  * object.
86  */
MapField_create(zend_class_entry * class_type)87 static zend_object* MapField_create(zend_class_entry *class_type) {
88   MapField *intern = emalloc(sizeof(MapField));
89   zend_object_std_init(&intern->std, class_type);
90   intern->std.handlers = &MapField_object_handlers;
91   Arena_Init(&intern->arena);
92   intern->map = NULL;
93   // Skip object_properties_init(), we don't allow derived classes.
94   return &intern->std;
95 }
96 
97 /**
98  * MapField_dtor()
99  *
100  * Object handler to destroy a MapField. This releases all resources
101  * associated with the message. Note that it is possible to access a destroyed
102  * object from PHP in rare cases.
103  */
MapField_destructor(zend_object * obj)104 static void MapField_destructor(zend_object* obj) {
105   MapField* intern = (MapField*)obj;
106   ObjCache_Delete(intern->map);
107   zval_ptr_dtor(&intern->arena);
108   zend_object_std_dtor(&intern->std);
109 }
110 
111 /**
112  * MapField_compare_objects()
113  *
114  * Object handler for comparing two repeated field objects. Called whenever PHP
115  * code does:
116  *
117  *   $map1 == $map2
118  */
MapField_compare_objects(zval * map1,zval * map2)119 static int MapField_compare_objects(zval *map1, zval *map2) {
120   MapField* intern1 = (MapField*)Z_OBJ_P(map1);
121   MapField* intern2 = (MapField*)Z_OBJ_P(map2);
122 
123   return MapType_Eq(intern1->type, intern2->type) &&
124                  MapEq(intern1->map, intern2->map, intern1->type)
125              ? 0
126              : 1;
127 }
128 
129 /**
130  * MapField_clone_obj()
131  *
132  * Object handler for cloning an object in PHP. Called when PHP code does:
133  *
134  *   $map2 = clone $map1;
135  */
MapField_clone_obj(PROTO_VAL * object)136 static zend_object *MapField_clone_obj(PROTO_VAL *object) {
137   MapField* intern = PROTO_VAL_P(object);
138   upb_Arena *arena = Arena_Get(&intern->arena);
139   upb_Map *clone =
140       upb_Map_New(arena, intern->type.key_type, intern->type.val_type.type);
141   size_t iter = kUpb_Map_Begin;
142 
143   while (upb_MapIterator_Next(intern->map, &iter)) {
144     upb_MessageValue key = upb_MapIterator_Key(intern->map, iter);
145     upb_MessageValue val = upb_MapIterator_Value(intern->map, iter);
146     upb_Map_Set(clone, key, val, arena);
147   }
148 
149   zval ret;
150   MapField_GetPhpWrapper(&ret, clone, intern->type, &intern->arena);
151   return Z_OBJ_P(&ret);
152 }
153 
Map_GetPropertyPtrPtr(PROTO_VAL * object,PROTO_STR * member,int type,void ** cache_slot)154 static zval *Map_GetPropertyPtrPtr(PROTO_VAL *object, PROTO_STR *member,
155                                    int type, void **cache_slot) {
156   return NULL;  // We don't offer direct references to our properties.
157 }
158 
Map_GetProperties(PROTO_VAL * object)159 static HashTable *Map_GetProperties(PROTO_VAL *object) {
160   return NULL;  // We do not have a properties table.
161 }
162 
163 // C Functions from map.h //////////////////////////////////////////////////////
164 
165 // These are documented in the header file.
166 
MapField_GetPhpWrapper(zval * val,upb_Map * map,MapField_Type type,zval * arena)167 void MapField_GetPhpWrapper(zval *val, upb_Map *map, MapField_Type type,
168                             zval *arena) {
169   if (!map) {
170     ZVAL_NULL(val);
171     return;
172   }
173 
174   if (!ObjCache_Get(map, val)) {
175     MapField *intern = emalloc(sizeof(MapField));
176     zend_object_std_init(&intern->std, MapField_class_entry);
177     intern->std.handlers = &MapField_object_handlers;
178     ZVAL_COPY(&intern->arena, arena);
179     intern->map = map;
180     intern->type = type;
181     // Skip object_properties_init(), we don't allow derived classes.
182     ObjCache_Add(intern->map, &intern->std);
183     ZVAL_OBJ(val, &intern->std);
184   }
185 }
186 
MapField_GetUpbMap(zval * val,MapField_Type type,upb_Arena * arena)187 upb_Map *MapField_GetUpbMap(zval *val, MapField_Type type, upb_Arena *arena) {
188   if (Z_ISREF_P(val)) {
189     ZVAL_DEREF(val);
190   }
191 
192   if (Z_TYPE_P(val) == IS_ARRAY) {
193     upb_Map *map = upb_Map_New(arena, type.key_type, type.val_type.type);
194     HashTable *table = HASH_OF(val);
195     HashPosition pos;
196 
197     zend_hash_internal_pointer_reset_ex(table, &pos);
198 
199     while (true) {
200       zval php_key;
201       zval *php_val;
202       upb_MessageValue upb_key;
203       upb_MessageValue upb_val;
204 
205       zend_hash_get_current_key_zval_ex(table, &php_key, &pos);
206       php_val = zend_hash_get_current_data_ex(table, &pos);
207 
208       if (!php_val) return map;
209 
210       if (!Convert_PhpToUpb(&php_key, &upb_key, KeyType(type), arena) ||
211           !Convert_PhpToUpbAutoWrap(php_val, &upb_val, type.val_type, arena)) {
212         return NULL;
213       }
214 
215       upb_Map_Set(map, upb_key, upb_val, arena);
216       zend_hash_move_forward_ex(table, &pos);
217       zval_dtor(&php_key);
218     }
219   } else if (Z_TYPE_P(val) == IS_OBJECT &&
220              Z_OBJCE_P(val) == MapField_class_entry) {
221     MapField *intern = (MapField*)Z_OBJ_P(val);
222 
223     if (!MapType_Eq(intern->type, type)) {
224       php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field.");
225       return NULL;
226     }
227 
228     upb_Arena_Fuse(arena, Arena_Get(&intern->arena));
229     return intern->map;
230   } else {
231     php_error_docref(NULL, E_USER_ERROR, "Must be a map");
232     return NULL;
233   }
234 }
235 
MapEq(const upb_Map * m1,const upb_Map * m2,MapField_Type type)236 bool MapEq(const upb_Map *m1, const upb_Map *m2, MapField_Type type) {
237   size_t iter = kUpb_Map_Begin;
238 
239   if ((m1 == NULL) != (m2 == NULL)) return false;
240   if (m1 == NULL) return true;
241   if (upb_Map_Size(m1) != upb_Map_Size(m2)) return false;
242 
243   while (upb_MapIterator_Next(m1, &iter)) {
244     upb_MessageValue key = upb_MapIterator_Key(m1, iter);
245     upb_MessageValue val1 = upb_MapIterator_Value(m1, iter);
246     upb_MessageValue val2;
247 
248     if (!upb_Map_Get(m2, key, &val2)) return false;
249     if (!ValueEq(val1, val2, type.val_type)) return false;
250   }
251 
252   return true;
253 }
254 
255 
256 // MapField PHP methods ////////////////////////////////////////////////////////
257 
258 /**
259  * MapField::__construct()
260  *
261  * Constructs an instance of MapField.
262  * @param long Key type.
263  * @param long Value type.
264  * @param string Message/Enum class (message/enum value types only).
265  */
PHP_METHOD(MapField,__construct)266 PHP_METHOD(MapField, __construct) {
267   MapField *intern = (MapField*)Z_OBJ_P(getThis());
268   upb_Arena *arena = Arena_Get(&intern->arena);
269   zend_long key_type, val_type;
270   zend_class_entry* klass = NULL;
271 
272   if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|C", &key_type, &val_type,
273                             &klass) != SUCCESS) {
274     return;
275   }
276 
277   intern->type.key_type = pbphp_dtype_to_type(key_type);
278   intern->type.val_type.type = pbphp_dtype_to_type(val_type);
279   intern->type.val_type.desc = Descriptor_GetFromClassEntry(klass);
280 
281   // Check that the key type is an allowed type.
282   switch (intern->type.key_type) {
283     case kUpb_CType_Int32:
284     case kUpb_CType_Int64:
285     case kUpb_CType_UInt32:
286     case kUpb_CType_UInt64:
287     case kUpb_CType_Bool:
288     case kUpb_CType_String:
289     case kUpb_CType_Bytes:
290       // These are OK.
291       break;
292     default:
293       zend_error(E_USER_ERROR, "Invalid key type for map.");
294   }
295 
296   if (intern->type.val_type.type == kUpb_CType_Message && klass == NULL) {
297     php_error_docref(NULL, E_USER_ERROR,
298                      "Message/enum type must have concrete class.");
299     return;
300   }
301 
302   intern->map =
303       upb_Map_New(arena, intern->type.key_type, intern->type.val_type.type);
304   ObjCache_Add(intern->map, &intern->std);
305 }
306 
307 /**
308  * MapField::offsetExists(): bool
309  *
310  * Implements the ArrayAccess interface. Invoked when PHP code calls:
311  *
312  *   isset($map[$idx]);
313  *   empty($map[$idx]);
314  *
315  * @param long The index to be checked.
316  * @return bool True if the element at the given index exists.
317  */
PHP_METHOD(MapField,offsetExists)318 PHP_METHOD(MapField, offsetExists) {
319   MapField *intern = (MapField*)Z_OBJ_P(getThis());
320   zval *key;
321   upb_MessageValue upb_key;
322 
323   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
324       !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
325     return;
326   }
327 
328   RETURN_BOOL(upb_Map_Get(intern->map, upb_key, NULL));
329 }
330 
331 /**
332  * MapField::offsetGet(): mixed
333  *
334  * Implements the ArrayAccess interface. Invoked when PHP code calls:
335  *
336  *   $x = $map[$idx];
337  *
338  * @param long The index of the element to be fetched.
339  * @return object The stored element at given index.
340  * @exception Invalid type for index.
341  * @exception Non-existing index.
342  */
PHP_METHOD(MapField,offsetGet)343 PHP_METHOD(MapField, offsetGet) {
344   MapField *intern = (MapField*)Z_OBJ_P(getThis());
345   zval *key;
346   zval ret;
347   upb_MessageValue upb_key, upb_val;
348 
349   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
350       !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
351     return;
352   }
353 
354   if (!upb_Map_Get(intern->map, upb_key, &upb_val)) {
355     zend_error(E_USER_ERROR, "Given key doesn't exist.");
356     return;
357   }
358 
359   Convert_UpbToPhp(upb_val, &ret, intern->type.val_type, &intern->arena);
360   RETURN_COPY_VALUE(&ret);
361 }
362 
363 /**
364  * MapField::offsetSet(): void
365  *
366  * Implements the ArrayAccess interface. Invoked when PHP code calls:
367  *
368  *   $map[$idx] = $x;
369  *
370  * @param long The index of the element to be assigned.
371  * @param object The element to be assigned.
372  * @exception Invalid type for index.
373  * @exception Non-existing index.
374  * @exception Incorrect type of the element.
375  */
PHP_METHOD(MapField,offsetSet)376 PHP_METHOD(MapField, offsetSet) {
377   MapField *intern = (MapField*)Z_OBJ_P(getThis());
378   upb_Arena *arena = Arena_Get(&intern->arena);
379   zval *key, *val;
380   upb_MessageValue upb_key, upb_val;
381 
382   if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS ||
383       !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL) ||
384       !Convert_PhpToUpb(val, &upb_val, intern->type.val_type, arena)) {
385     return;
386   }
387 
388   upb_Map_Set(intern->map, upb_key, upb_val, arena);
389 }
390 
391 /**
392  * MapField::offsetUnset(): void
393  *
394  * Implements the ArrayAccess interface. Invoked when PHP code calls:
395  *
396  *   unset($map[$idx]);
397  *
398  * @param long The index of the element to be removed.
399  * @exception Invalid type for index.
400  * @exception The element to be removed is not at the end of the MapField.
401  */
PHP_METHOD(MapField,offsetUnset)402 PHP_METHOD(MapField, offsetUnset) {
403   MapField *intern = (MapField*)Z_OBJ_P(getThis());
404   zval *key;
405   upb_MessageValue upb_key;
406 
407   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
408       !Convert_PhpToUpb(key, &upb_key, KeyType(intern->type), NULL)) {
409     return;
410   }
411 
412   upb_Map_Delete(intern->map, upb_key);
413 }
414 
415 /**
416  * MapField::count(): int
417  *
418  * Implements the Countable interface. Invoked when PHP code calls:
419  *
420  *   $len = count($map);
421  * Return the number of stored elements.
422  * This will also be called for: count($map)
423  * @return long The number of stored elements.
424  */
PHP_METHOD(MapField,count)425 PHP_METHOD(MapField, count) {
426   MapField *intern = (MapField*)Z_OBJ_P(getThis());
427 
428   if (zend_parse_parameters_none() == FAILURE) {
429     return;
430   }
431 
432   RETURN_LONG(upb_Map_Size(intern->map));
433 }
434 
435 /**
436  * MapField::getIterator(): Traversable
437  *
438  * Implements the IteratorAggregate interface. Invoked when PHP code calls:
439  *
440  *   foreach ($arr) {}
441  *
442  * @return object Beginning iterator.
443  */
PHP_METHOD(MapField,getIterator)444 PHP_METHOD(MapField, getIterator) {
445   zval ret;
446   MapFieldIter_make(&ret, getThis());
447   RETURN_COPY_VALUE(&ret);
448 }
449 
450 ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 2)
451   ZEND_ARG_INFO(0, key_type)
452   ZEND_ARG_INFO(0, value_type)
453   ZEND_ARG_INFO(0, value_class)
454 ZEND_END_ARG_INFO()
455 
456 
457 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_offsetGet, 0, 0, IS_MIXED, 1)
458   ZEND_ARG_INFO(0, index)
459 ZEND_END_ARG_INFO()
460 
461 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_offsetSet, 0, 2, IS_VOID, 0)
462   ZEND_ARG_INFO(0, index)
463   ZEND_ARG_INFO(0, newval)
464 ZEND_END_ARG_INFO()
465 
466 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_offsetUnset, 0, 0, IS_VOID, 0)
467   ZEND_ARG_INFO(0, index)
468 ZEND_END_ARG_INFO()
469 
470 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_offsetExists, 0, 0, _IS_BOOL, 0)
471   ZEND_ARG_INFO(0, index)
472 ZEND_END_ARG_INFO()
473 
474 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_getIterator, 0, 0, Traversable, 0)
475 ZEND_END_ARG_INFO()
476 
477 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_count, 0, 0, IS_LONG, 0)
478 ZEND_END_ARG_INFO()
479 
480 static zend_function_entry MapField_methods[] = {
481   PHP_ME(MapField, __construct,  arginfo_construct,    ZEND_ACC_PUBLIC)
482   PHP_ME(MapField, offsetExists, arginfo_offsetExists, ZEND_ACC_PUBLIC)
483   PHP_ME(MapField, offsetGet,    arginfo_offsetGet,    ZEND_ACC_PUBLIC)
484   PHP_ME(MapField, offsetSet,    arginfo_offsetSet,    ZEND_ACC_PUBLIC)
485   PHP_ME(MapField, offsetUnset,  arginfo_offsetUnset,  ZEND_ACC_PUBLIC)
486   PHP_ME(MapField, count,        arginfo_count,        ZEND_ACC_PUBLIC)
487   PHP_ME(MapField, getIterator,  arginfo_getIterator,  ZEND_ACC_PUBLIC)
488   ZEND_FE_END
489 };
490 
491 // -----------------------------------------------------------------------------
492 // MapFieldIter
493 // -----------------------------------------------------------------------------
494 
495 typedef struct {
496   zend_object std;
497   zval map_field;
498   size_t position;
499 } MapFieldIter;
500 
501 zend_class_entry *MapFieldIter_class_entry;
502 static zend_object_handlers MapFieldIter_object_handlers;
503 
504 /**
505  * MapFieldIter_create()
506  *
507  * PHP class entry function to allocate and initialize a new MapFieldIter
508  * object.
509  */
MapFieldIter_create(zend_class_entry * class_type)510 zend_object* MapFieldIter_create(zend_class_entry *class_type) {
511   MapFieldIter *intern = emalloc(sizeof(MapFieldIter));
512   zend_object_std_init(&intern->std, class_type);
513   intern->std.handlers = &MapFieldIter_object_handlers;
514   ZVAL_NULL(&intern->map_field);
515   intern->position = 0;
516   // Skip object_properties_init(), we don't allow derived classes.
517   return &intern->std;
518 }
519 
520 /**
521  * MapFieldIter_dtor()
522  *
523  * Object handler to destroy a MapFieldIter. This releases all resources
524  * associated with the message. Note that it is possible to access a destroyed
525  * object from PHP in rare cases.
526  */
map_field_iter_dtor(zend_object * obj)527 static void map_field_iter_dtor(zend_object* obj) {
528   MapFieldIter* intern = (MapFieldIter*)obj;
529   zval_ptr_dtor(&intern->map_field);
530   zend_object_std_dtor(&intern->std);
531 }
532 
533 /**
534  * MapFieldIter_make()
535  *
536  * Function to create a MapFieldIter directly from C.
537  */
MapFieldIter_make(zval * val,zval * map_field)538 static void MapFieldIter_make(zval *val, zval *map_field) {
539   MapFieldIter *iter;
540   ZVAL_OBJ(val,
541            MapFieldIter_class_entry->create_object(MapFieldIter_class_entry));
542   iter = (MapFieldIter*)Z_OBJ_P(val);
543   ZVAL_COPY(&iter->map_field, map_field);
544 }
545 
546 // -----------------------------------------------------------------------------
547 // PHP MapFieldIter Methods
548 // -----------------------------------------------------------------------------
549 
550 /*
551  * When a user writes:
552  *
553  *   foreach($arr as $key => $val) {}
554  *
555  * PHP translates this into:
556  *
557  *   $iter = $arr->getIterator();
558  *   for ($iter->rewind(); $iter->valid(); $iter->next()) {
559  *     $key = $iter->key();
560  *     $val = $iter->current();
561  *   }
562  */
563 
564 /**
565  * MapFieldIter::rewind(): void
566  *
567  * Implements the Iterator interface. Sets the iterator to the first element.
568  */
PHP_METHOD(MapFieldIter,rewind)569 PHP_METHOD(MapFieldIter, rewind) {
570   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
571   MapField *map_field = (MapField*)Z_OBJ_P(&intern->map_field);
572   intern->position = kUpb_Map_Begin;
573   upb_MapIterator_Next(map_field->map, &intern->position);
574 }
575 
576 /**
577  * MapFieldIter::current(): mixed
578  *
579  * Implements the Iterator interface. Returns the current value.
580  */
PHP_METHOD(MapFieldIter,current)581 PHP_METHOD(MapFieldIter, current) {
582   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
583   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
584   upb_MessageValue upb_val = upb_MapIterator_Value(field->map, intern->position);
585   zval ret;
586   Convert_UpbToPhp(upb_val, &ret, field->type.val_type, &field->arena);
587   RETURN_COPY_VALUE(&ret);
588 }
589 
590 /**
591  * MapFieldIter::key()
592  *
593  * Implements the Iterator interface. Returns the current key.
594  */
PHP_METHOD(MapFieldIter,key)595 PHP_METHOD(MapFieldIter, key) {
596   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
597   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
598   upb_MessageValue upb_key = upb_MapIterator_Key(field->map, intern->position);
599   zval ret;
600   Convert_UpbToPhp(upb_key, &ret, KeyType(field->type), NULL);
601   RETURN_COPY_VALUE(&ret);
602 }
603 
604 /**
605  * MapFieldIter::next(): void
606  *
607  * Implements the Iterator interface. Advances to the next element.
608  */
PHP_METHOD(MapFieldIter,next)609 PHP_METHOD(MapFieldIter, next) {
610   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
611   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
612   upb_MapIterator_Next(field->map, &intern->position);
613 }
614 
615 /**
616  * MapFieldIter::valid(): bool
617  *
618  * Implements the Iterator interface. Returns true if this is a valid element.
619  */
PHP_METHOD(MapFieldIter,valid)620 PHP_METHOD(MapFieldIter, valid) {
621   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
622   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
623   bool done = upb_MapIterator_Done(field->map, intern->position);
624   RETURN_BOOL(!done);
625 }
626 
627 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_rewind, 0, 0, IS_VOID, 0)
628 ZEND_END_ARG_INFO()
629 
630 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_current, 0, 0, IS_MIXED, 0)
631 ZEND_END_ARG_INFO()
632 
633 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_key, 0, 0, IS_MIXED, 0)
634 ZEND_END_ARG_INFO()
635 
636 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_next, 0, 0, IS_VOID, 0)
637 ZEND_END_ARG_INFO()
638 
639 PROTOBUF_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_valid, 0, 0, _IS_BOOL, 0)
640 ZEND_END_ARG_INFO()
641 
642 static zend_function_entry map_field_iter_methods[] = {
643   PHP_ME(MapFieldIter, rewind,      arginfo_rewind,  ZEND_ACC_PUBLIC)
644   PHP_ME(MapFieldIter, current,     arginfo_current, ZEND_ACC_PUBLIC)
645   PHP_ME(MapFieldIter, key,         arginfo_key,     ZEND_ACC_PUBLIC)
646   PHP_ME(MapFieldIter, next,        arginfo_next,    ZEND_ACC_PUBLIC)
647   PHP_ME(MapFieldIter, valid,       arginfo_valid,   ZEND_ACC_PUBLIC)
648   ZEND_FE_END
649 };
650 
651 // -----------------------------------------------------------------------------
652 // Module init.
653 // -----------------------------------------------------------------------------
654 
655 /**
656  * Map_ModuleInit()
657  *
658  * Called when the C extension is loaded to register all types.
659  */
660 
Map_ModuleInit()661 void Map_ModuleInit() {
662   zend_class_entry tmp_ce;
663   zend_object_handlers *h;
664 
665   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapField",
666                    MapField_methods);
667 
668   MapField_class_entry = zend_register_internal_class(&tmp_ce);
669   zend_class_implements(MapField_class_entry, 3, zend_ce_arrayaccess,
670                         zend_ce_aggregate, zend_ce_countable);
671   MapField_class_entry->ce_flags |= ZEND_ACC_FINAL;
672   MapField_class_entry->create_object = MapField_create;
673 
674   h = &MapField_object_handlers;
675   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
676   h->dtor_obj = MapField_destructor;
677 #if PHP_VERSION_ID < 80000
678   h->compare_objects = MapField_compare_objects;
679 #else
680   h->compare = MapField_compare_objects;
681 #endif
682   h->clone_obj = MapField_clone_obj;
683   h->get_properties = Map_GetProperties;
684   h->get_property_ptr_ptr = Map_GetPropertyPtrPtr;
685 
686   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapFieldIter",
687                    map_field_iter_methods);
688 
689   MapFieldIter_class_entry = zend_register_internal_class(&tmp_ce);
690   zend_class_implements(MapFieldIter_class_entry, 1, zend_ce_iterator);
691   MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
692   MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
693   MapFieldIter_class_entry->create_object = MapFieldIter_create;
694 
695   h = &MapFieldIter_object_handlers;
696   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
697   h->dtor_obj = map_field_iter_dtor;
698 }
699