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