Bläddra i källkod

Add map iterator for c extension (#3350)

Paul Yang 8 år sedan
förälder
incheckning
3a0382e907

+ 91 - 2
php/ext/google/protobuf/map.c

@@ -143,6 +143,7 @@ static zend_function_entry map_field_methods[] = {
   PHP_ME(MapField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
   PHP_ME(MapField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
   PHP_ME(MapField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
   ZEND_FE_END
 };
 
@@ -156,7 +157,10 @@ static void map_field_write_dimension(zval *object, zval *key,
 // -----------------------------------------------------------------------------
 
 zend_class_entry* map_field_type;
+zend_class_entry* map_field_iter_type;
+
 zend_object_handlers* map_field_handlers;
+zend_object_handlers* map_field_iter_handlers;
 
 static void map_begin_internal(Map *map, MapIter *iter) {
   iter->self = map;
@@ -231,8 +235,8 @@ PHP_PROTO_OBJECT_CREATE_END(Map, map_field)
 // Init class entry.
 PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapField", Map,
                            map_field)
-zend_class_implements(map_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess,
-                      spl_ce_Countable);
+zend_class_implements(map_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess,
+                      zend_ce_aggregate, spl_ce_Countable);
 map_field_handlers->write_dimension = map_field_write_dimension;
 map_field_handlers->get_gc = map_field_get_gc;
 PHP_PROTO_INIT_CLASS_END
@@ -444,6 +448,15 @@ PHP_METHOD(MapField, count) {
   RETURN_LONG(upb_strtable_count(&intern->table));
 }
 
+PHP_METHOD(MapField, getIterator) {
+  CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(return_value,
+                                   map_field_iter_type);
+
+  Map *intern = UNBOX(Map, getThis());
+  MapIter *iter = UNBOX(MapIter, return_value);
+  map_begin(getThis(), iter TSRMLS_CC);
+}
+
 // -----------------------------------------------------------------------------
 // Map Iterator
 // -----------------------------------------------------------------------------
@@ -470,3 +483,79 @@ upb_value map_iter_value(MapIter *iter, int *len) {
   *len = native_slot_size(iter->self->value_type);
   return upb_strtable_iter_value(&iter->it);
 }
+
+// -----------------------------------------------------------------------------
+// MapFieldIter methods
+// -----------------------------------------------------------------------------
+static zend_function_entry map_field_iter_methods[] = {
+  PHP_ME(MapFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// MapFieldIter creation/desctruction
+// -----------------------------------------------------------------------------
+
+// Define object free method.
+PHP_PROTO_OBJECT_FREE_START(MapIter, map_field_iter)
+PHP_PROTO_OBJECT_FREE_END
+
+PHP_PROTO_OBJECT_DTOR_START(MapIter, map_field_iter)
+PHP_PROTO_OBJECT_DTOR_END
+
+// Define object create method.
+PHP_PROTO_OBJECT_CREATE_START(MapIter, map_field_iter)
+intern->self = NULL;
+PHP_PROTO_OBJECT_CREATE_END(MapIter, map_field_iter)
+
+// Init class entry.
+PHP_PROTO_INIT_CLASS_START("Google\\Protobuf\\Internal\\MapFieldIter",
+                           MapIter, map_field_iter)
+zend_class_implements(map_field_iter_type TSRMLS_CC, 1, zend_ce_iterator);
+PHP_PROTO_INIT_CLASS_END
+
+// -----------------------------------------------------------------------------
+// PHP MapFieldIter Methods
+// -----------------------------------------------------------------------------
+
+PHP_METHOD(MapFieldIter, rewind) {
+  MapIter *intern = UNBOX(MapIter, getThis());
+  map_begin_internal(intern->self, intern);
+}
+
+PHP_METHOD(MapFieldIter, current) {
+  MapIter *intern = UNBOX(MapIter, getThis());
+  Map *map_field = intern->self;
+
+  int value_length = 0;
+  upb_value value = map_iter_value(intern, &value_length);
+
+  void* mem = upb_value_memory(&value);
+  native_slot_get_by_array(map_field->value_type, mem,
+                           ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
+}
+
+PHP_METHOD(MapFieldIter, key) {
+  MapIter *intern = UNBOX(MapIter, getThis());
+  Map *map_field = intern->self;
+
+  int key_length = 0;
+  const char* key = map_iter_key(intern, &key_length);
+
+  native_slot_get_by_map_key(map_field->key_type, key, key_length,
+                             ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
+}
+
+PHP_METHOD(MapFieldIter, next) {
+  MapIter *intern = UNBOX(MapIter, getThis());
+  map_next(intern);
+}
+
+PHP_METHOD(MapFieldIter, valid) {
+  MapIter *intern = UNBOX(MapIter, getThis());
+  RETURN_BOOL(!map_done(intern));
+}

+ 2 - 0
php/ext/google/protobuf/protobuf.c

@@ -189,6 +189,7 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) {
 
 static PHP_MINIT_FUNCTION(protobuf) {
   map_field_init(TSRMLS_C);
+  map_field_iter_init(TSRMLS_C);
   repeated_field_init(TSRMLS_C);
   repeated_field_iter_init(TSRMLS_C);
   gpb_type_init(TSRMLS_C);
@@ -206,6 +207,7 @@ static PHP_MSHUTDOWN_FUNCTION(protobuf) {
   PEFREE(repeated_field_handlers);
   PEFREE(repeated_field_iter_handlers);
   PEFREE(map_field_handlers);
+  PEFREE(map_field_iter_handlers);
 
   return 0;
 }

+ 13 - 2
php/ext/google/protobuf/protobuf.h

@@ -373,6 +373,7 @@ struct MessageLayout;
 struct RepeatedField;
 struct RepeatedFieldIter;
 struct Map;
+struct MapIter;
 struct Oneof;
 
 typedef struct DescriptorPool DescriptorPool;
@@ -385,6 +386,7 @@ typedef struct MessageLayout MessageLayout;
 typedef struct RepeatedField RepeatedField;
 typedef struct RepeatedFieldIter RepeatedFieldIter;
 typedef struct Map Map;
+typedef struct MapIter MapIter;
 typedef struct Oneof Oneof;
 
 // -----------------------------------------------------------------------------
@@ -400,6 +402,7 @@ void enum_descriptor_init(TSRMLS_D);
 void descriptor_pool_init(TSRMLS_D);
 void gpb_type_init(TSRMLS_D);
 void map_field_init(TSRMLS_D);
+void map_field_iter_init(TSRMLS_D);
 void repeated_field_init(TSRMLS_D);
 void repeated_field_iter_init(TSRMLS_D);
 void util_init(TSRMLS_D);
@@ -659,6 +662,7 @@ void native_slot_get_default(upb_fieldtype_t type,
 // -----------------------------------------------------------------------------
 
 extern zend_object_handlers* map_field_handlers;
+extern zend_object_handlers* map_field_iter_handlers;
 
 PHP_PROTO_WRAP_OBJECT_START(Map)
   upb_fieldtype_t key_type;
@@ -667,10 +671,10 @@ PHP_PROTO_WRAP_OBJECT_START(Map)
   upb_strtable table;
 PHP_PROTO_WRAP_OBJECT_END
 
-typedef struct {
+PHP_PROTO_WRAP_OBJECT_START(MapIter)
   Map* self;
   upb_strtable_iter it;
-} MapIter;
+PHP_PROTO_WRAP_OBJECT_END
 
 void map_begin(zval* self, MapIter* iter TSRMLS_DC);
 void map_next(MapIter* iter);
@@ -709,6 +713,13 @@ PHP_METHOD(MapField, offsetGet);
 PHP_METHOD(MapField, offsetSet);
 PHP_METHOD(MapField, offsetUnset);
 PHP_METHOD(MapField, count);
+PHP_METHOD(MapField, getIterator);
+
+PHP_METHOD(MapFieldIter, rewind);
+PHP_METHOD(MapFieldIter, current);
+PHP_METHOD(MapFieldIter, key);
+PHP_METHOD(MapFieldIter, next);
+PHP_METHOD(MapFieldIter, valid);
 
 // -----------------------------------------------------------------------------
 // Repeated Field.

+ 13 - 0
php/ext/google/protobuf/storage.c

@@ -355,6 +355,19 @@ void native_slot_get_by_array(upb_fieldtype_t type, const void* memory,
   }
 }
 
+void native_slot_get_by_map_key(upb_fieldtype_t type, const void* memory,
+                                int length, CACHED_VALUE* cache TSRMLS_DC) {
+  switch (type) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      PHP_PROTO_ZVAL_STRINGL(CACHED_PTR_TO_ZVAL_PTR(cache), memory, length, 1);
+      return;
+    }
+    default:
+      native_slot_get(type, memory, cache TSRMLS_CC);
+  }
+}
+
 void native_slot_get_default(upb_fieldtype_t type,
                              CACHED_VALUE* cache TSRMLS_DC) {
   switch (type) {

+ 4 - 1
php/src/Google/Protobuf/Internal/MapFieldIter.php

@@ -91,9 +91,12 @@ class MapFieldIter implements \Iterator
     public function key()
     {
         $key = key($this->container);
-        // PHP associative array stores bool as integer for key.
         if ($this->key_type === GPBType::BOOL) {
+            // PHP associative array stores bool as integer for key.
             return boolval($key);
+        } elseif ($this->key_type === GPBType::STRING) {
+            // PHP associative array stores int string as int for key.
+            return strval($key);
         } else {
             return $key;
         }

+ 34 - 0
php/tests/map_field_test.php

@@ -56,6 +56,23 @@ class MapFieldTest extends PHPUnit_Framework_TestCase {
         unset($arr['3.1']);
         unset($arr[MAX_INT32_STRING]);
         $this->assertEquals(0, count($arr));
+
+        // Test foreach.
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+        for ($i = 0; $i < 3; $i++) {
+          $arr[$i] = $i;
+        }
+        $i = 0;
+        $arr_test = [];
+        foreach ($arr as $key => $val) {
+          $this->assertSame($key, $val);
+          $arr_test[] = $key;
+          $i++;
+        }
+        $this->assertTrue(isset($arr_test[0]));
+        $this->assertTrue(isset($arr_test[1]));
+        $this->assertTrue(isset($arr_test[2]));
+        $this->assertSame(3, $i);
     }
 
     #########################################################
@@ -366,6 +383,23 @@ class MapFieldTest extends PHPUnit_Framework_TestCase {
         $this->assertEquals(1, count($arr));
         unset($arr[True]);
         $this->assertEquals(0, count($arr));
+
+        // Test foreach.
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+        for ($i = 0; $i < 3; $i++) {
+          $arr[$i] = $i;
+        }
+        $i = 0;
+        $arr_test = [];
+        foreach ($arr as $key => $val) {
+          $this->assertSame($key, $val);
+          $arr_test[] = $key;
+          $i++;
+        }
+        $this->assertTrue(isset($arr_test['0']));
+        $this->assertTrue(isset($arr_test['1']));
+        $this->assertTrue(isset($arr_test['2']));
+        $this->assertSame(3, $i);
     }
 
     #########################################################