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