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