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