diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c index 215dcd467c..e4a88c393d 100644 --- a/php/ext/google/protobuf/array.c +++ b/php/ext/google/protobuf/array.c @@ -54,6 +54,16 @@ static zend_function_entry repeated_field_methods[] = { PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC) PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_function_entry repeated_field_iter_methods[] = { + PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -70,11 +80,15 @@ static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC); static HashTable *repeated_field_get_gc(zval *object, zval ***table, int *n TSRMLS_DC); +static zend_object_value repeated_field_iter_create(zend_class_entry *ce TSRMLS_DC); +static void repeated_field_iter_free(void *object TSRMLS_DC); + // ----------------------------------------------------------------------------- // RepeatedField creation/desctruction // ----------------------------------------------------------------------------- zend_class_entry* repeated_field_type; +zend_class_entry* repeated_field_iter_type; zend_object_handlers* repeated_field_handlers; void repeated_field_init(TSRMLS_D) { @@ -86,8 +100,8 @@ void repeated_field_init(TSRMLS_D) { repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC); repeated_field_type->create_object = repeated_field_create; - zend_class_implements(repeated_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess, - spl_ce_Countable); + zend_class_implements(repeated_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess, + zend_ce_aggregate, spl_ce_Countable); repeated_field_handlers = PEMALLOC(zend_object_handlers); memcpy(repeated_field_handlers, zend_get_std_object_handlers(), @@ -386,3 +400,112 @@ PHP_METHOD(RepeatedField, count) { RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array))); } + +/** + * Return the beginning iterator. + * This will also be called for: foreach($arr) + * @return object Beginning iterator. + */ +PHP_METHOD(RepeatedField, getIterator) { + zval *iter_php = NULL; + MAKE_STD_ZVAL(iter_php); + Z_TYPE_P(iter_php) = IS_OBJECT; + Z_OBJVAL_P(iter_php) = repeated_field_iter_type->create_object( + repeated_field_iter_type TSRMLS_CC); + + RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RepeatedFieldIter *iter = zend_object_store_get_object(iter_php TSRMLS_CC); + iter->repeated_field = intern; + iter->position = 0; + + RETURN_ZVAL(iter_php, 1, 1); +} + +// ----------------------------------------------------------------------------- +// RepeatedFieldIter creation/desctruction +// ----------------------------------------------------------------------------- + +void repeated_field_iter_init(TSRMLS_D) { + zend_class_entry class_type; + const char* class_name = "Google\\Protobuf\\Internal\\RepeatedFieldIter"; + INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name), + repeated_field_iter_methods); + + repeated_field_iter_type = + zend_register_internal_class(&class_type TSRMLS_CC); + repeated_field_iter_type->create_object = repeated_field_iter_create; + + zend_class_implements(repeated_field_iter_type TSRMLS_CC, 1, + zend_ce_iterator); +} + +static zend_object_value repeated_field_iter_create( + zend_class_entry *ce TSRMLS_DC) { + zend_object_value retval = {0}; + RepeatedFieldIter *intern; + + intern = emalloc(sizeof(RepeatedFieldIter)); + memset(intern, 0, sizeof(RepeatedFieldIter)); + + zend_object_std_init(&intern->std, ce TSRMLS_CC); + object_properties_init(&intern->std, ce); + + intern->repeated_field = NULL; + intern->position = 0; + + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + (zend_objects_free_object_storage_t)repeated_field_iter_free, + NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + + return retval; +} + +static void repeated_field_iter_free(void *object TSRMLS_DC) { + RepeatedFieldIter *intern = object; + zend_object_std_dtor(&intern->std TSRMLS_CC); + efree(object); +} + +// ----------------------------------------------------------------------------- +// PHP RepeatedFieldIter Methods +// ----------------------------------------------------------------------------- + +PHP_METHOD(RepeatedFieldIter, rewind) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + intern->position = 0; +} + +PHP_METHOD(RepeatedFieldIter, current) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RepeatedField *repeated_field = intern->repeated_field; + + long index; + void *memory; + + HashTable *table = HASH_OF(repeated_field->array); + + if (zend_hash_index_find(table, intern->position, (void **)&memory) == + FAILURE) { + zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); + return; + } + native_slot_get(repeated_field->type, memory, return_value_ptr TSRMLS_CC); +} + +PHP_METHOD(RepeatedFieldIter, key) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_LONG(intern->position); +} + +PHP_METHOD(RepeatedFieldIter, next) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + ++intern->position; +} + +PHP_METHOD(RepeatedFieldIter, valid) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_BOOL(zend_hash_num_elements(HASH_OF(intern->repeated_field->array)) > + intern->position); +} diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c index 019bca2981..ea85b999f8 100644 --- a/php/ext/google/protobuf/protobuf.c +++ b/php/ext/google/protobuf/protobuf.c @@ -156,6 +156,7 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) { static PHP_MINIT_FUNCTION(protobuf) { map_field_init(TSRMLS_C); repeated_field_init(TSRMLS_C); + repeated_field_iter_init(TSRMLS_C); gpb_type_init(TSRMLS_C); message_init(TSRMLS_C); descriptor_pool_init(TSRMLS_C); diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 8a1d9261d7..93027bc1ed 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -51,6 +51,7 @@ struct MessageField; struct MessageHeader; struct MessageLayout; struct RepeatedField; +struct RepeatedFieldIter; struct MapField; typedef struct DescriptorPool DescriptorPool; @@ -61,6 +62,7 @@ typedef struct MessageField MessageField; typedef struct MessageHeader MessageHeader; typedef struct MessageLayout MessageLayout; typedef struct RepeatedField RepeatedField; +typedef struct RepeatedFieldIter RepeatedFieldIter; typedef struct MapField MapField; // ----------------------------------------------------------------------------- @@ -77,6 +79,7 @@ void descriptor_pool_init(TSRMLS_D); void gpb_type_init(TSRMLS_D); void map_field_init(TSRMLS_D); void repeated_field_init(TSRMLS_D); +void repeated_field_iter_init(TSRMLS_D); void util_init(TSRMLS_D); void message_init(TSRMLS_D); @@ -366,6 +369,12 @@ struct RepeatedField { // (for message field only). }; +struct RepeatedFieldIter { + zend_object std; + RepeatedField* repeated_field; + long position; +}; + void repeated_field_create_with_type(zend_class_entry* ce, const upb_fielddef* field, zval** repeated_field TSRMLS_DC); @@ -383,6 +392,13 @@ PHP_METHOD(RepeatedField, offsetGet); PHP_METHOD(RepeatedField, offsetSet); PHP_METHOD(RepeatedField, offsetUnset); PHP_METHOD(RepeatedField, count); +PHP_METHOD(RepeatedField, getIterator); + +PHP_METHOD(RepeatedFieldIter, rewind); +PHP_METHOD(RepeatedFieldIter, current); +PHP_METHOD(RepeatedFieldIter, key); +PHP_METHOD(RepeatedFieldIter, next); +PHP_METHOD(RepeatedFieldIter, valid); // ----------------------------------------------------------------------------- // Oneof Field. diff --git a/php/tests/array_test.php b/php/tests/array_test.php index a118b54cf8..a79a08bc59 100644 --- a/php/tests/array_test.php +++ b/php/tests/array_test.php @@ -65,6 +65,17 @@ class RepeatedFieldTest extends PHPUnit_Framework_TestCase $this->assertSame(3, $arr[6]); $arr [7]= MAX_INT32_STRING; $this->assertSame(MAX_INT32, $arr[7]); + + // Test foreach. + $arr = new RepeatedField(GPBType::INT32); + for ($i = 0; $i < 3; $i++) { + $arr []= $i; + } + $i = 0; + foreach ($arr as $val) { + $this->assertSame($i++, $val); + } + $this->assertSame(3, $i); } /** diff --git a/tests.sh b/tests.sh index 1335c06bf2..7a12f267d9 100755 --- a/tests.sh +++ b/tests.sh @@ -408,7 +408,8 @@ build_php5.6_c() { build_php5.6_mac() { # Install PHP curl -s https://php-osx.liip.ch/install.sh | bash -s 5.6 - export PATH="/usr/local/php5-5.6.25-20160831-101628/bin:$PATH" + PHP_FOLDER=`find /usr/local -type d -name "php5-5.6*"` # The folder name may change upon time + export PATH="$PHP_FOLDER/bin:$PATH" # Install phpunit curl https://phar.phpunit.de/phpunit.phar -L -o phpunit.phar