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