PHP: Added `==` operators for Map and Array. (#7900)

pull/7908/head
Joshua Haberman 4 years ago committed by GitHub
parent 3abbbd8870
commit cae85a9f11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 43
      php/ext/google/protobuf/array.c
  2. 5
      php/ext/google/protobuf/array.h
  3. 45
      php/ext/google/protobuf/map.c
  4. 3
      php/ext/google/protobuf/map.h
  5. 51
      php/ext/google/protobuf/message.c
  6. 3
      php/ext/google/protobuf/message.h
  7. 1
      php/ext/google/protobuf/php-upb.c
  8. 31
      php/tests/ArrayTest.php
  9. 29
      php/tests/MapFieldTest.php

@ -41,6 +41,7 @@
#include "arena.h"
#include "convert.h"
#include "def.h"
#include "message.h"
#include "php-upb.h"
#include "protobuf.h"
@ -94,6 +95,26 @@ static void RepeatedField_destructor(zend_object* obj) {
zend_object_std_dtor(&intern->std);
}
/**
* RepeatedField_compare_objects()
*
* Object handler for comparing two repeated field objects. Called whenever PHP
* code does:
*
* $rf1 == $rf2
*/
static int RepeatedField_compare_objects(zval *rf1, zval *rf2) {
RepeatedField* intern1 = (RepeatedField*)Z_OBJ_P(rf1);
RepeatedField* intern2 = (RepeatedField*)Z_OBJ_P(rf2);
upb_fieldtype_t type = intern1->type;
const upb_msgdef *m = intern1->desc ? intern1->desc->msgdef : NULL;
if (type != intern2->type) return 1;
if (intern1->desc != intern2->desc) return 1;
return ArrayEq(intern1->array, intern2->array, type, m) ? 0 : 1;
}
static HashTable *RepeatedField_GetProperties(PROTO_VAL *object) {
return NULL; // We do not have a properties table.
}
@ -177,6 +198,27 @@ upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f,
}
}
bool ArrayEq(const upb_array *a1, const upb_array *a2, upb_fieldtype_t type,
const upb_msgdef *m) {
size_t i;
size_t n;
if ((a1 == NULL) != (a2 == NULL)) return false;
if (a1 == NULL) return true;
n = upb_array_size(a1);
if (n != upb_array_size(a2)) return false;
for (i = 0; i < n; i++) {
upb_msgval val1 = upb_array_get(a1, i);
upb_msgval val2 = upb_array_get(a2, i);
if (!ValueEq(val1, val2, type, m)) return false;
}
return true;
}
// RepeatedField PHP methods ///////////////////////////////////////////////////
/**
@ -594,6 +636,7 @@ void Array_ModuleInit() {
h = &RepeatedField_object_handlers;
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
h->dtor_obj = RepeatedField_destructor;
h->compare_objects = RepeatedField_compare_objects;
h->get_properties = RepeatedField_GetProperties;
h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr;

@ -58,4 +58,9 @@ upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, upb_arena
void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
const upb_fielddef *f, zval *arena);
// Returns true if the given arrays are equal. Both arrays must be of this
// |type| and, if the type is |UPB_TYPE_MESSAGE|, must have the same |m|.
bool ArrayEq(const upb_array *a1, const upb_array *a2, upb_fieldtype_t type,
const upb_msgdef *m);
#endif // PHP_PROTOBUF_ARRAY_H_

@ -37,6 +37,7 @@
#include "arena.h"
#include "convert.h"
#include "message.h"
#include "php-upb.h"
#include "protobuf.h"
@ -90,6 +91,28 @@ static void MapField_destructor(zend_object* obj) {
zend_object_std_dtor(&intern->std);
}
/**
* MapField_compare_objects()
*
* Object handler for comparing two repeated field objects. Called whenever PHP
* code does:
*
* $map1 == $map2
*/
static int MapField_compare_objects(zval *map1, zval *map2) {
MapField* intern1 = (MapField*)Z_OBJ_P(map1);
MapField* intern2 = (MapField*)Z_OBJ_P(map2);
const upb_msgdef *m = intern1->desc ? intern1->desc->msgdef : NULL;
upb_fieldtype_t key_type = intern1->key_type;
upb_fieldtype_t val_type = intern1->val_type;
if (key_type != intern2->key_type) return 1;
if (val_type != intern2->val_type) return 1;
if (intern1->desc != intern2->desc) return 1;
return MapEq(intern1->map, intern2->map, key_type, val_type, m) ? 0 : 1;
}
static zval *Map_GetPropertyPtrPtr(PROTO_VAL *object, PROTO_STR *member,
int type, void **cache_slot) {
return NULL; // We don't offer direct references to our properties.
@ -185,6 +208,27 @@ upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena)
}
}
bool MapEq(const upb_map *m1, const upb_map *m2, upb_fieldtype_t key_type,
upb_fieldtype_t val_type, const upb_msgdef *m) {
size_t iter = UPB_MAP_BEGIN;
if ((m1 == NULL) != (m2 == NULL)) return false;
if (m1 == NULL) return true;
if (upb_map_size(m1) != upb_map_size(m2)) return false;
while (upb_mapiter_next(m1, &iter)) {
upb_msgval key = upb_mapiter_key(m1, iter);
upb_msgval val1 = upb_mapiter_value(m1, iter);
upb_msgval val2;
if (!upb_map_get(m2, key, &val2)) return false;
if (!ValueEq(val1, val2, val_type, m)) return false;
}
return true;
}
// MapField PHP methods ////////////////////////////////////////////////////////
/**
@ -578,6 +622,7 @@ void Map_ModuleInit() {
h = &MapField_object_handlers;
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
h->dtor_obj = MapField_destructor;
h->compare_objects = MapField_compare_objects;
h->get_properties = Map_GetProperties;
h->get_property_ptr_ptr = Map_GetPropertyPtrPtr;

@ -57,4 +57,7 @@ upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena);
void MapField_GetPhpWrapper(zval *val, upb_map *arr, const upb_fielddef *f,
zval *arena);
bool MapEq(const upb_map *m1, const upb_map *m2, upb_fieldtype_t key_type,
upb_fieldtype_t val_type, const upb_msgdef *m);
#endif // PHP_PROTOBUF_MAP_H_

@ -113,8 +113,8 @@ static bool MessageEq(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m)
/**
* ValueEq()()
*/
static bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
const upb_msgdef *m) {
bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
const upb_msgdef *m) {
switch (type) {
case UPB_TYPE_BOOL:
return val1.bool_val == val2.bool_val;
@ -140,53 +140,6 @@ static bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
}
}
/**
* MapEq()
*/
static bool MapEq(const upb_map *m1, const upb_map *m2,
upb_fieldtype_t key_type, upb_fieldtype_t val_type,
const upb_msgdef *m) {
size_t iter = UPB_MAP_BEGIN;
if ((m1 == NULL) != (m2 == NULL)) return false;
if (m1 == NULL) return true;
if (upb_map_size(m1) != upb_map_size(m2)) return false;
while (upb_mapiter_next(m1, &iter)) {
upb_msgval key = upb_mapiter_key(m1, iter);
upb_msgval val1 = upb_mapiter_value(m1, iter);
upb_msgval val2;
if (!upb_map_get(m2, key, &val2)) return false;
if (!ValueEq(val1, val2, val_type, m)) return false;
}
return true;
}
/**
* ArrayEq()
*/
static bool ArrayEq(const upb_array *a1, const upb_array *a2,
upb_fieldtype_t type, const upb_msgdef *m) {
size_t i;
size_t n;
if ((a1 == NULL) != (a2 == NULL)) return false;
if (a1 == NULL) return true;
n = upb_array_size(a1);
if (n != upb_array_size(a2)) return false;
for (i = 0; i < n; i++) {
upb_msgval val1 = upb_array_get(a1, i);
upb_msgval val2 = upb_array_get(a2, i);
if (!ValueEq(val1, val2, type, m)) return false;
}
return true;
}
/**
* MessageEq()
*/

@ -56,4 +56,7 @@ bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
zval *arena);
bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
const upb_msgdef *m);
#endif // PHP_PROTOBUF_MESSAGE_H_

@ -407,6 +407,7 @@ static const char *decode_varint64(upb_decstate *d, const char *ptr,
}
}
UPB_FORCEINLINE
static const char *decode_varint32(upb_decstate *d, const char *ptr,
const char *limit, uint32_t *val) {
uint64_t u64;

@ -590,4 +590,35 @@ class ArrayTest extends \PHPUnit\Framework\TestCase
$end = memory_get_usage();
$this->assertLessThan($start, $end);
}
#########################################################
# Test equality
#########################################################
public function testEquality()
{
$arr = new RepeatedField(GPBType::INT32);
$arr2 = new RepeatedField(GPBType::INT32);
$this->assertTrue($arr == $arr2);
$arr[] = 0;
$arr[] = 1;
$arr[] = 2;
$this->assertFalse($arr == $arr2);
$arr2[] = 0;
$arr2[] = 1;
$arr2[] = 2;
$this->assertTrue($arr == $arr2);
// Arrays of different types always compare false.
$this->assertFalse(new RepeatedField(GPBType::INT32) ==
new RepeatedField(GPBType::INT64));
$this->assertFalse(
new RepeatedField(GPBType::MESSAGE, TestMessage::class) ==
new RepeatedField(GPBType::MESSAGE, Sub::class));
}
}

@ -479,6 +479,35 @@ class MapFieldTest extends \PHPUnit\Framework\TestCase {
$m->setMapInt32Message($values);
}
#########################################################
# Test equality
#########################################################
public function testEquality()
{
$map = new MapField(GPBType::INT32, GPBType::INT32);
$map2 = new MapField(GPBType::INT32, GPBType::INT32);
$this->assertTrue($map == $map2);
$map[1] = 2;
$this->assertFalse($map == $map2);
$map2[1] = 2;
$this->assertTrue($map == $map2);
// Arrays of different types always compare false.
$this->assertFalse(new MapField(GPBType::INT32, GPBType::INT32) ==
new MapField(GPBType::INT32, GPBType::INT64));
$this->assertFalse(new MapField(GPBType::INT32, GPBType::INT32) ==
new MapField(GPBType::INT64, GPBType::INT32));
$this->assertFalse(
new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class) ==
new MapField(GPBType::INT32, GPBType::MESSAGE, Sub::class));
}
#########################################################
# Test memory leak
#########################################################

Loading…
Cancel
Save