• 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 "php-upb.h"
41 #include "protobuf.h"
42 
43 static void MapFieldIter_make(zval *val, zval *map_field);
44 
45 // -----------------------------------------------------------------------------
46 // MapField
47 // -----------------------------------------------------------------------------
48 
49 typedef struct {
50   zend_object std;
51   zval arena;
52   upb_map *map;
53   upb_fieldtype_t key_type;
54   upb_fieldtype_t val_type;
55   const Descriptor* desc;  // When values are messages.
56 } MapField;
57 
58 zend_class_entry *MapField_class_entry;
59 static zend_object_handlers MapField_object_handlers;
60 
61 // PHP Object Handlers /////////////////////////////////////////////////////////
62 
63 /**
64  * MapField_create()
65  *
66  * PHP class entry function to allocate and initialize a new MapField
67  * object.
68  */
MapField_create(zend_class_entry * class_type)69 static zend_object* MapField_create(zend_class_entry *class_type) {
70   MapField *intern = emalloc(sizeof(MapField));
71   zend_object_std_init(&intern->std, class_type);
72   intern->std.handlers = &MapField_object_handlers;
73   Arena_Init(&intern->arena);
74   intern->map = NULL;
75   // Skip object_properties_init(), we don't allow derived classes.
76   return &intern->std;
77 }
78 
79 /**
80  * MapField_dtor()
81  *
82  * Object handler to destroy a MapField. This releases all resources
83  * associated with the message. Note that it is possible to access a destroyed
84  * object from PHP in rare cases.
85  */
MapField_destructor(zend_object * obj)86 static void MapField_destructor(zend_object* obj) {
87   MapField* intern = (MapField*)obj;
88   ObjCache_Delete(intern->map);
89   zval_ptr_dtor(&intern->arena);
90   zend_object_std_dtor(&intern->std);
91 }
92 
Map_GetPropertyPtrPtr(PROTO_VAL * object,PROTO_STR * member,int type,void ** cache_slot)93 static zval *Map_GetPropertyPtrPtr(PROTO_VAL *object, PROTO_STR *member,
94                                    int type, void **cache_slot) {
95   return NULL;  // We don't offer direct references to our properties.
96 }
97 
Map_GetProperties(PROTO_VAL * object)98 static HashTable *Map_GetProperties(PROTO_VAL *object) {
99   return NULL;  // We do not have a properties table.
100 }
101 
102 // C Functions from map.h //////////////////////////////////////////////////////
103 
104 // These are documented in the header file.
105 
MapField_GetPhpWrapper(zval * val,upb_map * map,const upb_fielddef * f,zval * arena)106 void MapField_GetPhpWrapper(zval *val, upb_map *map, const upb_fielddef *f,
107                             zval *arena) {
108   if (!map) {
109     ZVAL_NULL(val);
110     return;
111   }
112 
113   if (!ObjCache_Get(map, val)) {
114     const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
115     const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
116     const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
117     MapField *intern = emalloc(sizeof(MapField));
118     zend_object_std_init(&intern->std, MapField_class_entry);
119     intern->std.handlers = &MapField_object_handlers;
120     ZVAL_COPY(&intern->arena, arena);
121     intern->map = map;
122     intern->key_type = upb_fielddef_type(key_f);
123     intern->val_type = upb_fielddef_type(val_f);
124     intern->desc = Descriptor_GetFromFieldDef(val_f);
125     // Skip object_properties_init(), we don't allow derived classes.
126     ObjCache_Add(intern->map, &intern->std);
127     ZVAL_OBJ(val, &intern->std);
128   }
129 }
130 
MapField_GetUpbMap(zval * val,const upb_fielddef * f,upb_arena * arena)131 upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena) {
132   const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
133   const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
134   const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
135   upb_fieldtype_t key_type = upb_fielddef_type(key_f);
136   upb_fieldtype_t val_type = upb_fielddef_type(val_f);
137   const Descriptor *desc = Descriptor_GetFromFieldDef(val_f);
138 
139   if (Z_ISREF_P(val)) {
140     ZVAL_DEREF(val);
141   }
142 
143   if (Z_TYPE_P(val) == IS_ARRAY) {
144     upb_map *map = upb_map_new(arena, key_type, val_type);
145     HashTable *table = HASH_OF(val);
146     HashPosition pos;
147 
148     zend_hash_internal_pointer_reset_ex(table, &pos);
149 
150     while (true) {
151       zval php_key;
152       zval *php_val;
153       upb_msgval upb_key;
154       upb_msgval upb_val;
155 
156       zend_hash_get_current_key_zval_ex(table, &php_key, &pos);
157       php_val = zend_hash_get_current_data_ex(table, &pos);
158 
159       if (!php_val) return map;
160 
161       if (!Convert_PhpToUpb(&php_key, &upb_key, key_type, NULL, arena) ||
162           !Convert_PhpToUpbAutoWrap(php_val, &upb_val, val_type, desc, arena)) {
163         return NULL;
164       }
165 
166       upb_map_set(map, upb_key, upb_val, arena);
167       zend_hash_move_forward_ex(table, &pos);
168       zval_dtor(&php_key);
169     }
170   } else if (Z_TYPE_P(val) == IS_OBJECT &&
171              Z_OBJCE_P(val) == MapField_class_entry) {
172     MapField *intern = (MapField*)Z_OBJ_P(val);
173 
174     if (intern->key_type != key_type || intern->val_type != val_type ||
175         intern->desc != desc) {
176       php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field.");
177       return NULL;
178     }
179 
180     upb_arena_fuse(arena, Arena_Get(&intern->arena));
181     return intern->map;
182   } else {
183     php_error_docref(NULL, E_USER_ERROR, "Must be a map");
184     return NULL;
185   }
186 }
187 
188 // MapField PHP methods ////////////////////////////////////////////////////////
189 
190 /**
191  * MapField::__construct()
192  *
193  * Constructs an instance of MapField.
194  * @param long Key type.
195  * @param long Value type.
196  * @param string Message/Enum class (message/enum value types only).
197  */
PHP_METHOD(MapField,__construct)198 PHP_METHOD(MapField, __construct) {
199   MapField *intern = (MapField*)Z_OBJ_P(getThis());
200   upb_arena *arena = Arena_Get(&intern->arena);
201   zend_long key_type, val_type;
202   zend_class_entry* klass = NULL;
203 
204   if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|C", &key_type, &val_type,
205                             &klass) != SUCCESS) {
206     return;
207   }
208 
209   intern->key_type = pbphp_dtype_to_type(key_type);
210   intern->val_type = pbphp_dtype_to_type(val_type);
211   intern->desc = Descriptor_GetFromClassEntry(klass);
212 
213   // Check that the key type is an allowed type.
214   switch (intern->key_type) {
215     case UPB_TYPE_INT32:
216     case UPB_TYPE_INT64:
217     case UPB_TYPE_UINT32:
218     case UPB_TYPE_UINT64:
219     case UPB_TYPE_BOOL:
220     case UPB_TYPE_STRING:
221     case UPB_TYPE_BYTES:
222       // These are OK.
223       break;
224     default:
225       zend_error(E_USER_ERROR, "Invalid key type for map.");
226   }
227 
228   if (intern->val_type == UPB_TYPE_MESSAGE && klass == NULL) {
229     php_error_docref(NULL, E_USER_ERROR,
230                      "Message/enum type must have concrete class.");
231     return;
232   }
233 
234   intern->map = upb_map_new(arena, intern->key_type, intern->val_type);
235   ObjCache_Add(intern->map, &intern->std);
236 }
237 
238 /**
239  * MapField::offsetExists()
240  *
241  * Implements the ArrayAccess interface. Invoked when PHP code calls:
242  *
243  *   isset($map[$idx]);
244  *   empty($map[$idx]);
245  *
246  * @param long The index to be checked.
247  * @return bool True if the element at the given index exists.
248  */
PHP_METHOD(MapField,offsetExists)249 PHP_METHOD(MapField, offsetExists) {
250   MapField *intern = (MapField*)Z_OBJ_P(getThis());
251   zval *key;
252   upb_msgval upb_key;
253 
254   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
255       !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
256     return;
257   }
258 
259   RETURN_BOOL(upb_map_get(intern->map, upb_key, NULL));
260 }
261 
262 /**
263  * MapField::offsetGet()
264  *
265  * Implements the ArrayAccess interface. Invoked when PHP code calls:
266  *
267  *   $x = $map[$idx];
268  *
269  * @param long The index of the element to be fetched.
270  * @return object The stored element at given index.
271  * @exception Invalid type for index.
272  * @exception Non-existing index.
273  */
PHP_METHOD(MapField,offsetGet)274 PHP_METHOD(MapField, offsetGet) {
275   MapField *intern = (MapField*)Z_OBJ_P(getThis());
276   zval *key;
277   zval ret;
278   upb_msgval upb_key, upb_val;
279 
280   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
281       !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
282     return;
283   }
284 
285   if (!upb_map_get(intern->map, upb_key, &upb_val)) {
286     zend_error(E_USER_ERROR, "Given key doesn't exist.");
287     return;
288   }
289 
290   Convert_UpbToPhp(upb_val, &ret, intern->val_type, intern->desc, &intern->arena);
291   RETURN_ZVAL(&ret, 0, 1);
292 }
293 
294 /**
295  * MapField::offsetSet()
296  *
297  * Implements the ArrayAccess interface. Invoked when PHP code calls:
298  *
299  *   $map[$idx] = $x;
300  *
301  * @param long The index of the element to be assigned.
302  * @param object The element to be assigned.
303  * @exception Invalid type for index.
304  * @exception Non-existing index.
305  * @exception Incorrect type of the element.
306  */
PHP_METHOD(MapField,offsetSet)307 PHP_METHOD(MapField, offsetSet) {
308   MapField *intern = (MapField*)Z_OBJ_P(getThis());
309   upb_arena *arena = Arena_Get(&intern->arena);
310   zval *key, *val;
311   upb_msgval upb_key, upb_val;
312 
313   if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS ||
314       !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL) ||
315       !Convert_PhpToUpb(val, &upb_val, intern->val_type, intern->desc, arena)) {
316     return;
317   }
318 
319   upb_map_set(intern->map, upb_key, upb_val, arena);
320 }
321 
322 /**
323  * MapField::offsetUnset()
324  *
325  * Implements the ArrayAccess interface. Invoked when PHP code calls:
326  *
327  *   unset($map[$idx]);
328  *
329  * @param long The index of the element to be removed.
330  * @exception Invalid type for index.
331  * @exception The element to be removed is not at the end of the MapField.
332  */
PHP_METHOD(MapField,offsetUnset)333 PHP_METHOD(MapField, offsetUnset) {
334   MapField *intern = (MapField*)Z_OBJ_P(getThis());
335   zval *key;
336   upb_msgval upb_key;
337 
338   if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
339       !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL)) {
340     return;
341   }
342 
343   upb_map_delete(intern->map, upb_key);
344 }
345 
346 /**
347  * MapField::count()
348  *
349  * Implements the Countable interface. Invoked when PHP code calls:
350  *
351  *   $len = count($map);
352  * Return the number of stored elements.
353  * This will also be called for: count($map)
354  * @return long The number of stored elements.
355  */
PHP_METHOD(MapField,count)356 PHP_METHOD(MapField, count) {
357   MapField *intern = (MapField*)Z_OBJ_P(getThis());
358 
359   if (zend_parse_parameters_none() == FAILURE) {
360     return;
361   }
362 
363   RETURN_LONG(upb_map_size(intern->map));
364 }
365 
366 /**
367  * MapField::getIterator()
368  *
369  * Implements the IteratorAggregate interface. Invoked when PHP code calls:
370  *
371  *   foreach ($arr) {}
372  *
373  * @return object Beginning iterator.
374  */
PHP_METHOD(MapField,getIterator)375 PHP_METHOD(MapField, getIterator) {
376   zval ret;
377   MapFieldIter_make(&ret, getThis());
378   RETURN_ZVAL(&ret, 0, 1);
379 }
380 
381 ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 2)
382   ZEND_ARG_INFO(0, key_type)
383   ZEND_ARG_INFO(0, value_type)
384   ZEND_ARG_INFO(0, value_class)
385 ZEND_END_ARG_INFO()
386 
387 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
388   ZEND_ARG_INFO(0, index)
389 ZEND_END_ARG_INFO()
390 
391 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
392   ZEND_ARG_INFO(0, index)
393   ZEND_ARG_INFO(0, newval)
394 ZEND_END_ARG_INFO()
395 
396 ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
397 ZEND_END_ARG_INFO()
398 
399 static zend_function_entry MapField_methods[] = {
400   PHP_ME(MapField, __construct,  arginfo_construct, ZEND_ACC_PUBLIC)
401   PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
402   PHP_ME(MapField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
403   PHP_ME(MapField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
404   PHP_ME(MapField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
405   PHP_ME(MapField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
406   PHP_ME(MapField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
407   ZEND_FE_END
408 };
409 
410 // -----------------------------------------------------------------------------
411 // MapFieldIter
412 // -----------------------------------------------------------------------------
413 
414 typedef struct {
415   zend_object std;
416   zval map_field;
417   size_t position;
418 } MapFieldIter;
419 
420 zend_class_entry *MapFieldIter_class_entry;
421 static zend_object_handlers MapFieldIter_object_handlers;
422 
423 /**
424  * MapFieldIter_create()
425  *
426  * PHP class entry function to allocate and initialize a new MapFieldIter
427  * object.
428  */
MapFieldIter_create(zend_class_entry * class_type)429 zend_object* MapFieldIter_create(zend_class_entry *class_type) {
430   MapFieldIter *intern = emalloc(sizeof(MapFieldIter));
431   zend_object_std_init(&intern->std, class_type);
432   intern->std.handlers = &MapFieldIter_object_handlers;
433   ZVAL_NULL(&intern->map_field);
434   intern->position = 0;
435   // Skip object_properties_init(), we don't allow derived classes.
436   return &intern->std;
437 }
438 
439 /**
440  * MapFieldIter_dtor()
441  *
442  * Object handler to destroy a MapFieldIter. This releases all resources
443  * associated with the message. Note that it is possible to access a destroyed
444  * object from PHP in rare cases.
445  */
map_field_iter_dtor(zend_object * obj)446 static void map_field_iter_dtor(zend_object* obj) {
447   MapFieldIter* intern = (MapFieldIter*)obj;
448   zval_ptr_dtor(&intern->map_field);
449   zend_object_std_dtor(&intern->std);
450 }
451 
452 /**
453  * MapFieldIter_make()
454  *
455  * Function to create a MapFieldIter directly from C.
456  */
MapFieldIter_make(zval * val,zval * map_field)457 static void MapFieldIter_make(zval *val, zval *map_field) {
458   MapFieldIter *iter;
459   ZVAL_OBJ(val,
460            MapFieldIter_class_entry->create_object(MapFieldIter_class_entry));
461   iter = (MapFieldIter*)Z_OBJ_P(val);
462   ZVAL_COPY(&iter->map_field, map_field);
463 }
464 
465 // -----------------------------------------------------------------------------
466 // PHP MapFieldIter Methods
467 // -----------------------------------------------------------------------------
468 
469 /*
470  * When a user writes:
471  *
472  *   foreach($arr as $key => $val) {}
473  *
474  * PHP translates this into:
475  *
476  *   $iter = $arr->getIterator();
477  *   for ($iter->rewind(); $iter->valid(); $iter->next()) {
478  *     $key = $iter->key();
479  *     $val = $iter->current();
480  *   }
481  */
482 
483 /**
484  * MapFieldIter::rewind()
485  *
486  * Implements the Iterator interface. Sets the iterator to the first element.
487  */
PHP_METHOD(MapFieldIter,rewind)488 PHP_METHOD(MapFieldIter, rewind) {
489   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
490   MapField *map_field = (MapField*)Z_OBJ_P(&intern->map_field);
491   intern->position = UPB_MAP_BEGIN;
492   upb_mapiter_next(map_field->map, &intern->position);
493 }
494 
495 /**
496  * MapFieldIter::current()
497  *
498  * Implements the Iterator interface. Returns the current value.
499  */
PHP_METHOD(MapFieldIter,current)500 PHP_METHOD(MapFieldIter, current) {
501   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
502   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
503   upb_msgval upb_val = upb_mapiter_value(field->map, intern->position);
504   zval ret;
505   Convert_UpbToPhp(upb_val, &ret, field->val_type, field->desc, &field->arena);
506   RETURN_ZVAL(&ret, 0, 1);
507 }
508 
509 /**
510  * MapFieldIter::key()
511  *
512  * Implements the Iterator interface. Returns the current key.
513  */
PHP_METHOD(MapFieldIter,key)514 PHP_METHOD(MapFieldIter, key) {
515   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
516   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
517   upb_msgval upb_key = upb_mapiter_key(field->map, intern->position);
518   zval ret;
519   Convert_UpbToPhp(upb_key, &ret, field->key_type, NULL, NULL);
520   RETURN_ZVAL(&ret, 0, 1);
521 }
522 
523 /**
524  * MapFieldIter::next()
525  *
526  * Implements the Iterator interface. Advances to the next element.
527  */
PHP_METHOD(MapFieldIter,next)528 PHP_METHOD(MapFieldIter, next) {
529   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
530   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
531   upb_mapiter_next(field->map, &intern->position);
532 }
533 
534 /**
535  * MapFieldIter::valid()
536  *
537  * Implements the Iterator interface. Returns true if this is a valid element.
538  */
PHP_METHOD(MapFieldIter,valid)539 PHP_METHOD(MapFieldIter, valid) {
540   MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
541   MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
542   bool done = upb_mapiter_done(field->map, intern->position);
543   RETURN_BOOL(!done);
544 }
545 
546 static zend_function_entry map_field_iter_methods[] = {
547   PHP_ME(MapFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
548   PHP_ME(MapFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
549   PHP_ME(MapFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
550   PHP_ME(MapFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
551   PHP_ME(MapFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
552   ZEND_FE_END
553 };
554 
555 // -----------------------------------------------------------------------------
556 // Module init.
557 // -----------------------------------------------------------------------------
558 
559 /**
560  * Map_ModuleInit()
561  *
562  * Called when the C extension is loaded to register all types.
563  */
564 
Map_ModuleInit()565 void Map_ModuleInit() {
566   zend_class_entry tmp_ce;
567   zend_object_handlers *h;
568 
569   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapField",
570                    MapField_methods);
571 
572   MapField_class_entry = zend_register_internal_class(&tmp_ce);
573   zend_class_implements(MapField_class_entry, 3, spl_ce_ArrayAccess,
574                         zend_ce_aggregate, spl_ce_Countable);
575   MapField_class_entry->ce_flags |= ZEND_ACC_FINAL;
576   MapField_class_entry->create_object = MapField_create;
577 
578   h = &MapField_object_handlers;
579   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
580   h->dtor_obj = MapField_destructor;
581   h->get_properties = Map_GetProperties;
582   h->get_property_ptr_ptr = Map_GetPropertyPtrPtr;
583 
584   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapFieldIter",
585                    map_field_iter_methods);
586 
587   MapFieldIter_class_entry = zend_register_internal_class(&tmp_ce);
588   zend_class_implements(MapFieldIter_class_entry, 1, zend_ce_iterator);
589   MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
590   MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
591   MapFieldIter_class_entry->create_object = MapFieldIter_create;
592 
593   h = &MapFieldIter_object_handlers;
594   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
595   h->dtor_obj = map_field_iter_dtor;
596 }
597