From b7ed2c0346d52b51f96aeb783b37b5b2b5a4a34e Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 22 Jul 2025 14:51:23 -0400 Subject: [PATCH 1/5] PHPC-2474: Support vector subtype for BSON Binary objects --- config.m4 | 1 + config.w32 | 2 +- php_phongo.c | 1 + src/BSON/Binary.c | 378 +++++++++++++++++- src/BSON/Binary.h | 7 + src/BSON/Binary.stub.php | 12 + src/BSON/Binary_arginfo.h | 33 +- src/BSON/VectorType.c | 28 ++ src/BSON/VectorType.stub.php | 15 + src/BSON/VectorType_arginfo.h | 15 + src/phongo_bson_encode.c | 1 + src/phongo_classes.h | 2 + src/phongo_structs.h | 2 +- tests/bson/bson-binary-constants.phpt | 2 + tests/bson/bson-binary-fromVector-001.phpt | 71 ++++ .../bson-binary-fromVector_error-001.phpt | 24 ++ .../bson-binary-fromVector_error-002.phpt | 30 ++ .../bson-binary-fromVector_error-003.phpt | 30 ++ tests/bson/bson-binary-getvectortype-001.phpt | 18 + tests/bson/bson-binary-serialization-002.phpt | 41 ++ tests/bson/bson-binary-set_state-001.phpt | 10 +- .../bson/bson-binary-set_state_error-004.phpt | 18 + tests/bson/bson-binary-toarray-001.phpt | 44 ++ tests/bson/bson-binary_error-005.phpt | 35 ++ tests/bson/bson-vectortype-001.phpt | 17 + 25 files changed, 815 insertions(+), 22 deletions(-) create mode 100644 src/BSON/VectorType.c create mode 100644 src/BSON/VectorType.stub.php create mode 100644 src/BSON/VectorType_arginfo.h create mode 100644 tests/bson/bson-binary-fromVector-001.phpt create mode 100644 tests/bson/bson-binary-fromVector_error-001.phpt create mode 100644 tests/bson/bson-binary-fromVector_error-002.phpt create mode 100644 tests/bson/bson-binary-fromVector_error-003.phpt create mode 100644 tests/bson/bson-binary-getvectortype-001.phpt create mode 100644 tests/bson/bson-binary-set_state_error-004.phpt create mode 100644 tests/bson/bson-binary-toarray-001.phpt create mode 100644 tests/bson/bson-binary_error-005.phpt create mode 100644 tests/bson/bson-vectortype-001.phpt diff --git a/config.m4 b/config.m4 index b2a182d32..40e6b862a 100644 --- a/config.m4 +++ b/config.m4 @@ -168,6 +168,7 @@ if test "$PHP_MONGODB" != "no"; then src/BSON/Unserializable.c \ src/BSON/UTCDateTime.c \ src/BSON/UTCDateTimeInterface.c \ + src/BSON/VectorType.c \ src/MongoDB/BulkWrite.c \ src/MongoDB/BulkWriteCommand.c \ src/MongoDB/BulkWriteCommandResult.c \ diff --git a/config.w32 b/config.w32 index 66979ce95..898f52488 100644 --- a/config.w32 +++ b/config.w32 @@ -116,7 +116,7 @@ if (PHP_MONGODB != "no") { EXTENSION("mongodb", "php_phongo.c", null, PHP_MONGODB_CFLAGS); MONGODB_ADD_SOURCES("/src", "phongo_apm.c phongo_atomic.c phongo_bson.c phongo_bson_encode.c phongo_client.c phongo_compat.c phongo_error.c phongo_execute.c phongo_ini.c phongo_log.c phongo_util.c"); - MONGODB_ADD_SOURCES("/src/BSON", "Binary.c BinaryInterface.c Document.c Iterator.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c PackedArray.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c"); + MONGODB_ADD_SOURCES("/src/BSON", "Binary.c BinaryInterface.c Document.c Iterator.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c PackedArray.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c VectorType.c"); MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c BulkWriteCommand.c BulkWriteCommandResult.c ClientEncryption.c Command.c Cursor.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c ServerDescription.c Session.c TopologyDescription.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c"); MONGODB_ADD_SOURCES("/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c BulkWriteCommandException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c EncryptionException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c UnexpectedValueException.c"); MONGODB_ADD_SOURCES("/src/MongoDB/Monitoring", "CommandFailedEvent.c CommandStartedEvent.c CommandSubscriber.c CommandSucceededEvent.c LogSubscriber.c SDAMSubscriber.c Subscriber.c ServerChangedEvent.c ServerClosedEvent.c ServerHeartbeatFailedEvent.c ServerHeartbeatStartedEvent.c ServerHeartbeatSucceededEvent.c ServerOpeningEvent.c TopologyChangedEvent.c TopologyClosedEvent.c TopologyOpeningEvent.c functions.c"); diff --git a/php_phongo.c b/php_phongo.c index 6c9bd29fc..0691c176e 100644 --- a/php_phongo.c +++ b/php_phongo.c @@ -248,6 +248,7 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */ php_phongo_timestamp_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_undefined_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_utcdatetime_init_ce(INIT_FUNC_ARGS_PASSTHRU); + php_phongo_vectortype_init_ce(INIT_FUNC_ARGS_PASSTHRU); php_phongo_cursor_interface_init_ce(INIT_FUNC_ARGS_PASSTHRU); diff --git a/src/BSON/Binary.c b/src/BSON/Binary.c index 4583ac8ab..20959bd06 100644 --- a/src/BSON/Binary.c +++ b/src/BSON/Binary.c @@ -16,16 +16,21 @@ #include #include +#include #include #include "php_phongo.h" +#include "phongo_bson_encode.h" #include "phongo_error.h" +#include "Binary.h" #include "Binary_arginfo.h" -#define PHONGO_BINARY_UUID_SIZE 16 - zend_class_entry* php_phongo_binary_ce; +static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const uint8_t* data, uint32_t data_len); +static phongo_bson_vector_type_t phongo_binary_get_vector_type(const php_phongo_binary_t* intern); +static void phongo_binary_get_vector_as_array(const php_phongo_binary_t* intern, zval* return_value); + /* Initialize the object and return whether it was successful. An exception will * be thrown on error. */ static bool php_phongo_binary_init(php_phongo_binary_t* intern, const char* data, size_t data_len, zend_long type) @@ -40,6 +45,11 @@ static bool php_phongo_binary_init(php_phongo_binary_t* intern, const char* data return false; } + if ((type == BSON_SUBTYPE_VECTOR) && phongo_binary_get_vector_type_from_data((const uint8_t*) data, data_len) == PHONGO_BSON_VECTOR_TYPE_INVALID) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Binary vector data is invalid"); + return false; + } + intern->data = estrndup(data, data_len); intern->data_len = data_len; intern->type = (uint8_t) type; @@ -272,8 +282,29 @@ static int php_phongo_binary_compare_objects(zval* o1, zval* o2) static HashTable* php_phongo_binary_get_debug_info(zend_object* object, int* is_temp) { - *is_temp = 1; - return php_phongo_binary_get_properties_hash(object, true); + *is_temp = 1; + HashTable* props = php_phongo_binary_get_properties_hash(object, true); + + php_phongo_binary_t* intern = Z_OBJ_BINARY(object); + + if (intern->type == BSON_SUBTYPE_VECTOR) { + zval vector; + + phongo_binary_get_vector_as_array(intern, &vector); + + if (EG(exception)) { + return props; + } + + zend_hash_str_update(props, "vector", sizeof("vector") - 1, &vector); + + zval vector_type; + + ZVAL_LONG(&vector_type, phongo_binary_get_vector_type(intern)); + zend_hash_str_update(props, "vectorType", sizeof("vectorType") - 1, &vector_type); + } + + return props; } static HashTable* php_phongo_binary_get_properties(zend_object* object) @@ -297,14 +328,339 @@ void php_phongo_binary_init_ce(INIT_FUNC_ARGS) bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type) { - php_phongo_binary_t* intern; - object_init_ex(object, php_phongo_binary_ce); - intern = Z_BINARY_OBJ_P(object); - intern->data = estrndup(data, data_len); - intern->data_len = data_len; - intern->type = (uint8_t) type; + return php_phongo_binary_init(Z_BINARY_OBJ_P(object), data, data_len, type); +} - return true; +static inline void phongo_binary_init_vector_from_bson_key(php_phongo_binary_t* intern, const bson_t* doc, const char* key) +{ + bson_iter_t iter; + + if (!(bson_iter_init_find(&iter, doc, key) && BSON_ITER_HOLDS_VECTOR(&iter))) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_iter_init_find failed to find binary vector in key \"%s\"", key); + return; + } + + uint32_t data_len; + const uint8_t* data; + + bson_iter_binary(&iter, NULL, &data_len, &data); + php_phongo_binary_init(intern, (const char*) data, data_len, BSON_SUBTYPE_VECTOR); +} + +static void phongo_binary_init_vector_from_float32_array(php_phongo_binary_t* intern, HashTable* vector) +{ + if (!zend_array_is_list(vector)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector to be a list"); + return; + } + + const size_t vector_len = zend_array_count(vector); + + bson_t doc = BSON_INITIALIZER; + bson_vector_float32_view_t view; + + if (!BSON_APPEND_VECTOR_FLOAT32_UNINIT(&doc, "vector", vector_len, &view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "BSON_APPEND_VECTOR_FLOAT32_UNINIT failed for vector of size %zu", vector_len); + return; + } + + zval* val; + size_t i = 0; + + ZEND_HASH_FOREACH_VAL_IND(vector, val) + { + if (Z_TYPE_P(val) != IS_DOUBLE) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be a float, %s given", i, zend_zval_type_name(val)); + return; + } + + float v = (float) Z_DVAL_P(val); + + if (!bson_vector_float32_view_write(view, &v, 1, i)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_vector_float32_view_write failed to write vector[%zu]", i); + return; + } + + i += 1; + } + ZEND_HASH_FOREACH_END(); + + phongo_binary_init_vector_from_bson_key(intern, &doc, "vector"); +} + +static void phongo_binary_init_vector_from_int8_array(php_phongo_binary_t* intern, HashTable* vector) +{ + if (!zend_array_is_list(vector)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector to be a list"); + return; + } + + const size_t vector_len = zend_array_count(vector); + + bson_t doc = BSON_INITIALIZER; + bson_vector_int8_view_t view; + + if (!BSON_APPEND_VECTOR_INT8_UNINIT(&doc, "vector", vector_len, &view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "BSON_APPEND_VECTOR_INT8_UNINIT failed for vector of size %zu", vector_len); + return; + } + + zval* val; + size_t i = 0; + + ZEND_HASH_FOREACH_VAL_IND(vector, val) + { + if (Z_TYPE_P(val) != IS_LONG) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be an integer, %s given", i, zend_zval_type_name(val)); + return; + } + + if (Z_LVAL_P(val) < INT8_MIN || Z_LVAL_P(val) > INT8_MAX) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be a signed 8-bit integer, %" PHONGO_LONG_FORMAT " given", i, Z_LVAL_P(val)); + return; + } + + int8_t v = (int8_t) Z_LVAL_P(val); + + if (!bson_vector_int8_view_write(view, &v, 1, i)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_vector_int8_view_write failed to write vector[%zu]", i); + return; + } + + i += 1; + } + ZEND_HASH_FOREACH_END(); + + phongo_binary_init_vector_from_bson_key(intern, &doc, "vector"); +} + +static void phongo_binary_init_vector_from_packed_bit_array(php_phongo_binary_t* intern, HashTable* vector) +{ + if (!zend_array_is_list(vector)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector to be a list"); + return; + } + + const size_t vector_len = zend_array_count(vector); + + bson_t doc = BSON_INITIALIZER; + bson_vector_packed_bit_view_t view; + + if (!BSON_APPEND_VECTOR_PACKED_BIT_UNINIT(&doc, "vector", vector_len, &view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "BSON_APPEND_VECTOR_PACKED_BIT_UNINIT failed for vector of size %zu", vector_len); + return; + } + + zval* val; + size_t i = 0; + + ZEND_HASH_FOREACH_VAL_IND(vector, val) + { + if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be an integer or boolean, %s given", i, zend_zval_type_name(val)); + return; + } + + if (Z_TYPE_P(val) == IS_LONG && (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > 1)) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be 0 or 1, %" PHONGO_LONG_FORMAT " given", i, Z_LVAL_P(val)); + return; + } + + bool v = zend_is_true(val); + + if (!bson_vector_packed_bit_view_pack_bool(view, &v, 1, i)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_vector_packed_bit_view_pack_bool failed to write vector[%zu]", i); + return; + } + + i += 1; + } + ZEND_HASH_FOREACH_END(); + + phongo_binary_init_vector_from_bson_key(intern, &doc, "vector"); +} + +static PHP_METHOD(MongoDB_BSON_Binary, fromVector) +{ + HashTable* vector; + zend_object* type; + + object_init_ex(return_value, php_phongo_binary_ce); + php_phongo_binary_t* intern = Z_BINARY_OBJ_P(return_value); + + PHONGO_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ARRAY_HT(vector) + Z_PARAM_OBJ_OF_CLASS(type, php_phongo_vectortype_ce) + PHONGO_PARSE_PARAMETERS_END(); + + zval *type_name = zend_enum_fetch_case_name(type); + + if (zend_string_equals_literal(Z_STR_P(type_name), "Float32")) { + phongo_binary_init_vector_from_float32_array(intern, vector); + return; + } + + if (zend_string_equals_literal(Z_STR_P(type_name), "Int8")) { + phongo_binary_init_vector_from_int8_array(intern, vector); + return; + } + + if (zend_string_equals_literal(Z_STR_P(type_name), "PackedBit")) { + phongo_binary_init_vector_from_packed_bit_array(intern, vector); + return; + } + + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Unsupported binary vector type: %s", Z_STR_P(type_name)); + RETURN_THROWS(); +} + +static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const uint8_t* data, uint32_t data_len) +{ + if (bson_vector_int8_const_view_init(NULL, data, data_len)) { + return PHONGO_BSON_VECTOR_TYPE_INT8; + } + + if (bson_vector_float32_const_view_init(NULL, data, data_len)) { + return PHONGO_BSON_VECTOR_TYPE_FLOAT32; + } + + if (bson_vector_packed_bit_const_view_init(NULL, data, data_len)) { + return PHONGO_BSON_VECTOR_TYPE_PACKED_BIT; + } + + return PHONGO_BSON_VECTOR_TYPE_INVALID; +} + +static phongo_bson_vector_type_t phongo_binary_get_vector_type(const php_phongo_binary_t* intern) +{ + return phongo_binary_get_vector_type_from_data((const uint8_t*) intern->data, intern->data_len); +} + +static PHP_METHOD(MongoDB_BSON_Binary, getVectorType) +{ + PHONGO_PARSE_PARAMETERS_NONE(); + + php_phongo_binary_t* intern = Z_BINARY_OBJ_P(getThis()); + + if (intern->type != BSON_SUBTYPE_VECTOR) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected Binary of type vector (%" PRId8 ") but it is %" PHONGO_LONG_FORMAT, BSON_SUBTYPE_VECTOR, intern->type); + RETURN_THROWS(); + } + + phongo_bson_vector_type_t type = phongo_binary_get_vector_type(Z_BINARY_OBJ_P(getThis())); + const char *type_case; + + switch (type) { + case PHONGO_BSON_VECTOR_TYPE_FLOAT32: + type_case = "Float32"; + break; + case PHONGO_BSON_VECTOR_TYPE_INT8: + type_case = "Int8"; + break; + case PHONGO_BSON_VECTOR_TYPE_PACKED_BIT: + type_case = "PackedBit"; + break; + default: + // The vector should always be valid by this point, but check for an error + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); + RETURN_THROWS(); + } + + RETVAL_OBJ_COPY(zend_enum_get_case_cstr(php_phongo_vectortype_ce, type_case)); +} + +static void phongo_binary_get_vector_as_array(const php_phongo_binary_t* intern, zval* return_value) +{ + phongo_bson_vector_type_t type = phongo_binary_get_vector_type(intern); + + // The vector should always be valid by this point, but check for an error + if (type == PHONGO_BSON_VECTOR_TYPE_INVALID) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); + RETURN_THROWS(); + } + + bson_t tmp_doc = BSON_INITIALIZER; + + if (type == PHONGO_BSON_VECTOR_TYPE_INT8) { + bson_vector_int8_const_view_t view; + + if (!bson_vector_int8_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_INT8(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + } else if (type == PHONGO_BSON_VECTOR_TYPE_FLOAT32) { + bson_vector_float32_const_view_t view; + + if (!bson_vector_float32_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + } else if (type == PHONGO_BSON_VECTOR_TYPE_PACKED_BIT) { + bson_vector_packed_bit_const_view_t view; + + if (!bson_vector_packed_bit_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + } + + bson_iter_t iter; + + if (!(bson_iter_init_find(&iter, &tmp_doc, "vector") && BSON_ITER_HOLDS_ARRAY(&iter))) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_iter_init_find failed for appended vector"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + + uint32_t data_len; + const uint8_t* data; + + bson_iter_array(&iter, &data_len, &data); + + bson_t tmp_vector = BSON_INITIALIZER; + + if (!bson_init_static(&tmp_vector, data, data_len)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "bson_init_static failed for appended vector"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + + php_phongo_bson_state state; + PHONGO_BSON_INIT_STATE(state); + state.is_visiting_array = true; + + if (!php_phongo_bson_to_zval_ex(&tmp_vector, &state)) { + // Exception already thrown + bson_destroy(&tmp_doc); + zval_ptr_dtor(&state.zchild); + php_phongo_bson_typemap_dtor(&state.map); + RETURN_THROWS(); + } + + bson_destroy(&tmp_doc); + php_phongo_bson_typemap_dtor(&state.map); + + RETURN_ZVAL(&state.zchild, 0, 1); +} + +static PHP_METHOD(MongoDB_BSON_Binary, toArray) +{ + PHONGO_PARSE_PARAMETERS_NONE(); + + php_phongo_binary_t* intern = Z_BINARY_OBJ_P(getThis()); + + if (intern->type != BSON_SUBTYPE_VECTOR) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected Binary of type vector (%" PRId8 ") but it is %" PHONGO_LONG_FORMAT, BSON_SUBTYPE_VECTOR, intern->type); + RETURN_THROWS(); + } + + phongo_binary_get_vector_as_array(Z_BINARY_OBJ_P(getThis()), return_value); } diff --git a/src/BSON/Binary.h b/src/BSON/Binary.h index cdaa179ae..9b6ab4def 100644 --- a/src/BSON/Binary.h +++ b/src/BSON/Binary.h @@ -19,6 +19,13 @@ #define PHONGO_BINARY_UUID_SIZE 16 +typedef enum { + PHONGO_BSON_VECTOR_TYPE_INVALID = 0, + PHONGO_BSON_VECTOR_TYPE_INT8 = 0x03, + PHONGO_BSON_VECTOR_TYPE_FLOAT32 = 0x27, + PHONGO_BSON_VECTOR_TYPE_PACKED_BIT = 0x10, +} phongo_bson_vector_type_t; + bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type); #endif /* PHONGO_BSON_BINARY_H */ diff --git a/src/BSON/Binary.stub.php b/src/BSON/Binary.stub.php index 134fac6c7..9eb7b00ee 100644 --- a/src/BSON/Binary.stub.php +++ b/src/BSON/Binary.stub.php @@ -63,6 +63,12 @@ final class Binary implements BinaryInterface, \JsonSerializable, Type, \Stringa */ public const TYPE_SENSITIVE = UNKNOWN; + /** + * @var int + * @cvalue BSON_SUBTYPE_VECTOR + */ + public const TYPE_VECTOR = UNKNOWN; + /** * @var int * @cvalue BSON_SUBTYPE_USER @@ -71,10 +77,16 @@ final class Binary implements BinaryInterface, \JsonSerializable, Type, \Stringa final public function __construct(string $data, int $type = Binary::TYPE_GENERIC) {} + final public static function fromVector(array $vector, VectorType $vectorType): Binary {} + final public function getData(): string {} final public function getType(): int {} + final public function getVectorType(): VectorType {} + + final public function toArray(): array {} + final public static function __set_state(array $properties): Binary {} final public function __toString(): string {} diff --git a/src/BSON/Binary_arginfo.h b/src/BSON/Binary_arginfo.h index 1363dc09d..7471b154e 100644 --- a/src/BSON/Binary_arginfo.h +++ b/src/BSON/Binary_arginfo.h @@ -1,17 +1,28 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a08dec3b63e23fbde350360b32fa0323e47069da */ + * Stub hash: 3c433c48b47c68051a55617ddbb9ed0f50c21484 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_BSON_Binary___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "MongoDB\\BSON\\Binary::TYPE_GENERIC") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_BSON_Binary_fromVector, 0, 2, MongoDB\\BSON\\Binary, 0) + ZEND_ARG_TYPE_INFO(0, vector, IS_ARRAY, 0) + ZEND_ARG_OBJ_INFO(0, vectorType, MongoDB\\BSON\\VectorType, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary_getData, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary_getType, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_BSON_Binary_getVectorType, 0, 0, MongoDB\\BSON\\VectorType, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary_toArray, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_BSON_Binary___set_state, 0, 1, MongoDB\\BSON\\Binary, 0) ZEND_ARG_TYPE_INFO(0, properties, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -22,27 +33,30 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary___unse ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary___serialize, 0, 0, IS_ARRAY, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_MongoDB_BSON_Binary___serialize arginfo_class_MongoDB_BSON_Binary_toArray ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary_jsonSerialize, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() - static ZEND_METHOD(MongoDB_BSON_Binary, __construct); +static ZEND_METHOD(MongoDB_BSON_Binary, fromVector); static ZEND_METHOD(MongoDB_BSON_Binary, getData); static ZEND_METHOD(MongoDB_BSON_Binary, getType); +static ZEND_METHOD(MongoDB_BSON_Binary, getVectorType); +static ZEND_METHOD(MongoDB_BSON_Binary, toArray); static ZEND_METHOD(MongoDB_BSON_Binary, __set_state); static ZEND_METHOD(MongoDB_BSON_Binary, __toString); static ZEND_METHOD(MongoDB_BSON_Binary, __unserialize); static ZEND_METHOD(MongoDB_BSON_Binary, __serialize); static ZEND_METHOD(MongoDB_BSON_Binary, jsonSerialize); - static const zend_function_entry class_MongoDB_BSON_Binary_methods[] = { ZEND_ME(MongoDB_BSON_Binary, __construct, arginfo_class_MongoDB_BSON_Binary___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_BSON_Binary, fromVector, arginfo_class_MongoDB_BSON_Binary_fromVector, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, getData, arginfo_class_MongoDB_BSON_Binary_getData, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, getType, arginfo_class_MongoDB_BSON_Binary_getType, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_BSON_Binary, getVectorType, arginfo_class_MongoDB_BSON_Binary_getVectorType, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + ZEND_ME(MongoDB_BSON_Binary, toArray, arginfo_class_MongoDB_BSON_Binary_toArray, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, __set_state, arginfo_class_MongoDB_BSON_Binary___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, __toString, arginfo_class_MongoDB_BSON_Binary___toString, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, __unserialize, arginfo_class_MongoDB_BSON_Binary___unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) @@ -56,8 +70,7 @@ static zend_class_entry *register_class_MongoDB_BSON_Binary(zend_class_entry *cl zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY(ce, "MongoDB\\BSON", "Binary", class_MongoDB_BSON_Binary_methods); - class_entry = zend_register_internal_class_ex(&ce, NULL); - class_entry->ce_flags |= ZEND_ACC_FINAL; + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); zend_class_implements(class_entry, 4, class_entry_MongoDB_BSON_BinaryInterface, class_entry_JsonSerializable, class_entry_MongoDB_BSON_Type, class_entry_Stringable); zval const_TYPE_GENERIC_value; @@ -114,6 +127,12 @@ static zend_class_entry *register_class_MongoDB_BSON_Binary(zend_class_entry *cl zend_declare_class_constant_ex(class_entry, const_TYPE_SENSITIVE_name, &const_TYPE_SENSITIVE_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_TYPE_SENSITIVE_name); + zval const_TYPE_VECTOR_value; + ZVAL_LONG(&const_TYPE_VECTOR_value, BSON_SUBTYPE_VECTOR); + zend_string *const_TYPE_VECTOR_name = zend_string_init_interned("TYPE_VECTOR", sizeof("TYPE_VECTOR") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TYPE_VECTOR_name, &const_TYPE_VECTOR_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TYPE_VECTOR_name); + zval const_TYPE_USER_DEFINED_value; ZVAL_LONG(&const_TYPE_USER_DEFINED_value, BSON_SUBTYPE_USER); zend_string *const_TYPE_USER_DEFINED_name = zend_string_init_interned("TYPE_USER_DEFINED", sizeof("TYPE_USER_DEFINED") - 1, 1); diff --git a/src/BSON/VectorType.c b/src/BSON/VectorType.c new file mode 100644 index 000000000..d3ce5f85f --- /dev/null +++ b/src/BSON/VectorType.c @@ -0,0 +1,28 @@ +/* + * Copyright 2014-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "php_phongo.h" +#include "VectorType_arginfo.h" + +zend_class_entry* php_phongo_vectortype_ce; + +void php_phongo_vectortype_init_ce(INIT_FUNC_ARGS) +{ + php_phongo_vectortype_ce = register_class_MongoDB_BSON_VectorType(); +} diff --git a/src/BSON/VectorType.stub.php b/src/BSON/VectorType.stub.php new file mode 100644 index 000000000..191e3fdfd --- /dev/null +++ b/src/BSON/VectorType.stub.php @@ -0,0 +1,15 @@ +milliseconds); return; } + // TODO: confirm that this handles binary vector if (instanceof_function(Z_OBJCE_P(object), php_phongo_binary_ce)) { php_phongo_binary_t* intern = Z_BINARY_OBJ_P(object); diff --git a/src/phongo_classes.h b/src/phongo_classes.h index 1bdb0fafb..87afdd34f 100644 --- a/src/phongo_classes.h +++ b/src/phongo_classes.h @@ -364,6 +364,7 @@ extern zend_class_entry* php_phongo_symbol_ce; extern zend_class_entry* php_phongo_timestamp_ce; extern zend_class_entry* php_phongo_undefined_ce; extern zend_class_entry* php_phongo_utcdatetime_ce; +extern zend_class_entry* php_phongo_vectortype_ce; extern zend_class_entry* php_phongo_binary_interface_ce; extern zend_class_entry* php_phongo_decimal128_interface_ce; @@ -412,6 +413,7 @@ extern void php_phongo_type_init_ce(INIT_FUNC_ARGS); extern void php_phongo_undefined_init_ce(INIT_FUNC_ARGS); extern void php_phongo_unserializable_init_ce(INIT_FUNC_ARGS); extern void php_phongo_utcdatetime_init_ce(INIT_FUNC_ARGS); +extern void php_phongo_vectortype_init_ce(INIT_FUNC_ARGS); extern void php_phongo_binary_interface_init_ce(INIT_FUNC_ARGS); extern void php_phongo_decimal128_interface_init_ce(INIT_FUNC_ARGS); diff --git a/src/phongo_structs.h b/src/phongo_structs.h index 7b083e612..2fc3c3ae2 100644 --- a/src/phongo_structs.h +++ b/src/phongo_structs.h @@ -194,7 +194,7 @@ typedef struct { typedef struct { char* data; - int data_len; + size_t data_len; uint8_t type; HashTable* properties; zend_object std; diff --git a/tests/bson/bson-binary-constants.phpt b/tests/bson/bson-binary-constants.phpt index 7b224769f..edf8358a2 100644 --- a/tests/bson/bson-binary-constants.phpt +++ b/tests/bson/bson-binary-constants.phpt @@ -14,6 +14,7 @@ var_dump(Binary::TYPE_MD5); var_dump(Binary::TYPE_ENCRYPTED); var_dump(Binary::TYPE_COLUMN); var_dump(Binary::TYPE_SENSITIVE); +var_dump(Binary::TYPE_VECTOR); var_dump(Binary::TYPE_USER_DEFINED); ?> @@ -29,5 +30,6 @@ int(5) int(6) int(7) int(8) +int(9) int(128) ===DONE=== diff --git a/tests/bson/bson-binary-fromVector-001.phpt b/tests/bson/bson-binary-fromVector-001.phpt new file mode 100644 index 000000000..d40d0ff54 --- /dev/null +++ b/tests/bson/bson-binary-fromVector-001.phpt @@ -0,0 +1,71 @@ +--TEST-- +MongoDB\BSON\Binary::fromVector() construction of various vector types +--FILE-- + +===DONE=== + +--EXPECTF-- +object(MongoDB\BSON\Binary)#%d (%d) { + ["data"]=> + string(18) "%a" + ["type"]=> + int(9) + ["vector"]=> + array(4) { + [0]=> + float(1) + [1]=> + float(-1) + [2]=> + float(0.5) + [3]=> + float(-0.5) + } + ["vectorType"]=> + int(39) +} +object(MongoDB\BSON\Binary)#%d (%d) { + ["data"]=> + string(6) "%a" + ["type"]=> + int(9) + ["vector"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + } + ["vectorType"]=> + int(3) +} +object(MongoDB\BSON\Binary)#%d (%d) { + ["data"]=> + string(3) "%a" + ["type"]=> + int(9) + ["vector"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(0) + [2]=> + int(1) + [3]=> + int(0) + } + ["vectorType"]=> + int(16) +} +===DONE=== diff --git a/tests/bson/bson-binary-fromVector_error-001.phpt b/tests/bson/bson-binary-fromVector_error-001.phpt new file mode 100644 index 000000000..efb47753c --- /dev/null +++ b/tests/bson/bson-binary-fromVector_error-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +MongoDB\BSON\Binary::fromVector() construction errors for VectorType::Float32 +--FILE-- + 1.0], MongoDB\BSON\VectorType::Float32); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([1.0, 2], MongoDB\BSON\VectorType::Float32); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector to be a list +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be a float, int given +===DONE=== diff --git a/tests/bson/bson-binary-fromVector_error-002.phpt b/tests/bson/bson-binary-fromVector_error-002.phpt new file mode 100644 index 000000000..679a739d0 --- /dev/null +++ b/tests/bson/bson-binary-fromVector_error-002.phpt @@ -0,0 +1,30 @@ +--TEST-- +MongoDB\BSON\Binary::fromVector() construction errors for VectorType::Int8 +--FILE-- + 1], MongoDB\BSON\VectorType::Int8); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([1, 2.0], MongoDB\BSON\VectorType::Int8); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([1, 256], MongoDB\BSON\VectorType::Int8); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector to be a list +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be an integer, float given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be a signed 8-bit integer, 256 given +===DONE=== diff --git a/tests/bson/bson-binary-fromVector_error-003.phpt b/tests/bson/bson-binary-fromVector_error-003.phpt new file mode 100644 index 000000000..aee8b0256 --- /dev/null +++ b/tests/bson/bson-binary-fromVector_error-003.phpt @@ -0,0 +1,30 @@ +--TEST-- +MongoDB\BSON\Binary::fromVector() construction errors for VectorType::PackedBit +--FILE-- + 1], MongoDB\BSON\VectorType::PackedBit); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([true, 1.0], MongoDB\BSON\VectorType::PackedBit); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([true, 2], MongoDB\BSON\VectorType::PackedBit); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector to be a list +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be an integer or boolean, float given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be 0 or 1, 2 given +===DONE=== diff --git a/tests/bson/bson-binary-getvectortype-001.phpt b/tests/bson/bson-binary-getvectortype-001.phpt new file mode 100644 index 000000000..bb7719609 --- /dev/null +++ b/tests/bson/bson-binary-getvectortype-001.phpt @@ -0,0 +1,18 @@ +--TEST-- +MongoDB\BSON\Binary::getVectorType() +--FILE-- +getVectorType()); +var_dump(MongoDB\BSON\Binary::fromVector([1, 2, 3, 4], MongoDB\BSON\VectorType::Int8)->getVectorType()); +var_dump(MongoDB\BSON\Binary::fromVector([1, 0, true, false], MongoDB\BSON\VectorType::PackedBit)->getVectorType()); + +?> +===DONE=== + +--EXPECT-- +enum(MongoDB\BSON\VectorType::Float32) +enum(MongoDB\BSON\VectorType::Int8) +enum(MongoDB\BSON\VectorType::PackedBit) +===DONE=== + diff --git a/tests/bson/bson-binary-serialization-002.phpt b/tests/bson/bson-binary-serialization-002.phpt index 1fe9be000..61277e476 100644 --- a/tests/bson/bson-binary-serialization-002.phpt +++ b/tests/bson/bson-binary-serialization-002.phpt @@ -9,6 +9,7 @@ $tests = [ ["\0foo", MongoDB\BSON\Binary::TYPE_GENERIC], [hex2bin('123e4567e89b12d3a456426655440000'), MongoDB\BSON\Binary::TYPE_UUID], [md5('foobar', true), MongoDB\BSON\Binary::TYPE_MD5], + [hex2bin('030001020304'), MongoDB\BSON\Binary::TYPE_VECTOR], ]; foreach ($tests as $test) { @@ -94,4 +95,44 @@ object(MongoDB\BSON\Binary)#%d (%d) { int(5) } +object(MongoDB\BSON\Binary)#%d (%d) { + ["data"]=> + string(6) "%a" + ["type"]=> + int(9) + ["vector"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + } + ["vectorType"]=> + int(3) +} +string(70) "O:19:"MongoDB\BSON\Binary":2:{s:4:"data";s:6:"%a";s:4:"type";i:9;}" +object(MongoDB\BSON\Binary)#%d (%d) { + ["data"]=> + string(6) "%a" + ["type"]=> + int(9) + ["vector"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + } + ["vectorType"]=> + int(3) +} + ===DONE=== diff --git a/tests/bson/bson-binary-set_state-001.phpt b/tests/bson/bson-binary-set_state-001.phpt index 84f2468f2..1564e639b 100644 --- a/tests/bson/bson-binary-set_state-001.phpt +++ b/tests/bson/bson-binary-set_state-001.phpt @@ -9,6 +9,7 @@ $tests = [ ["\0foo", MongoDB\BSON\Binary::TYPE_GENERIC], [hex2bin('123e4567e89b12d3a456426655440000'), MongoDB\BSON\Binary::TYPE_UUID], [md5('foobar', true), MongoDB\BSON\Binary::TYPE_MD5], + [hex2bin('030001020304'), MongoDB\BSON\Binary::TYPE_VECTOR], ]; foreach ($tests as $test) { @@ -41,13 +42,18 @@ foreach ($tests as $test) { )) %r\\?%rMongoDB\BSON\Binary::__set_state(array( - 'data' => '>Egè›Ó¤VBfUD' . "\0" . '' . "\0" . '', + 'data' => '%a', 'type' => 4, )) %r\\?%rMongoDB\BSON\Binary::__set_state(array( - 'data' => '8Xö"0¬<‘_0 fCÆ?', + 'data' => '%a', 'type' => 5, )) +%r\\?%rMongoDB\BSON\Binary::__set_state(array( + 'data' => '%a', + 'type' => 9, +)) + ===DONE=== diff --git a/tests/bson/bson-binary-set_state_error-004.phpt b/tests/bson/bson-binary-set_state_error-004.phpt new file mode 100644 index 000000000..34516707b --- /dev/null +++ b/tests/bson/bson-binary-set_state_error-004.phpt @@ -0,0 +1,18 @@ +--TEST-- +MongoDB\BSON\Binary::__set_state() requires valid vector data +--FILE-- + '', 'type' => MongoDB\BSON\Binary::TYPE_VECTOR]); +}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n"; + +?> +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Binary vector data is invalid +===DONE=== diff --git a/tests/bson/bson-binary-toarray-001.phpt b/tests/bson/bson-binary-toarray-001.phpt new file mode 100644 index 000000000..dc551ca25 --- /dev/null +++ b/tests/bson/bson-binary-toarray-001.phpt @@ -0,0 +1,44 @@ +--TEST-- +MongoDB\BSON\Binary::toArray() +--FILE-- +toArray()); +var_dump(MongoDB\BSON\Binary::fromVector([1, 2, 3, 4], MongoDB\BSON\VectorType::Int8)->toArray()); +var_dump(MongoDB\BSON\Binary::fromVector([1, 0, true, false], MongoDB\BSON\VectorType::PackedBit)->toArray()); + +?> +===DONE=== + +--EXPECT-- +array(4) { + [0]=> + float(1) + [1]=> + float(-1) + [2]=> + float(0.5) + [3]=> + float(-0.5) +} +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) +} +array(4) { + [0]=> + int(1) + [1]=> + int(0) + [2]=> + int(1) + [3]=> + int(0) +} +===DONE=== diff --git a/tests/bson/bson-binary_error-005.phpt b/tests/bson/bson-binary_error-005.phpt new file mode 100644 index 000000000..b35d8d5d2 --- /dev/null +++ b/tests/bson/bson-binary_error-005.phpt @@ -0,0 +1,35 @@ +--TEST-- +MongoDB\BSON\Binary vector methods argument count errors +--SKIPIF-- + +=', '7.99'); ?> +--FILE-- +toArray(2); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() use ($bv) { + $bv->getVectorType(2); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector(); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +?> +===DONE=== + +--EXPECTF-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +MongoDB\BSON\Binary::toArray() expects exactly 0 %r(argument|parameter)%rs, 1 given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +MongoDB\BSON\Binary::getVectorType() expects exactly 0 %r(argument|parameter)%rs, 1 given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +MongoDB\BSON\Binary::fromVector() expects exactly 2 %r(argument|parameter)%rs, 0 given +===DONE=== diff --git a/tests/bson/bson-vectortype-001.phpt b/tests/bson/bson-vectortype-001.phpt new file mode 100644 index 000000000..1feb22094 --- /dev/null +++ b/tests/bson/bson-vectortype-001.phpt @@ -0,0 +1,17 @@ +--TEST-- +MongoDB\BSON\VectorType +--FILE-- + +===DONE=== + +--EXPECT-- +enum(MongoDB\BSON\VectorType::Float32) +enum(MongoDB\BSON\VectorType::Int8) +enum(MongoDB\BSON\VectorType::PackedBit) +===DONE=== From 05b71e1510117eec31cad9d3a152ea2ac8a9ded2 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 30 Sep 2025 12:17:38 -0400 Subject: [PATCH 2/5] Relocate VectorType enum defs and revise test cases --- src/BSON/Binary.c | 67 +++++++------------ src/BSON/Binary.h | 7 -- src/BSON/VectorType.c | 34 +++++++++- src/BSON/VectorType.h | 34 ++++++++++ tests/bson/bson-binary-fromVector-001.phpt | 10 +-- .../bson-binary-fromVector_error-002.phpt | 10 ++- .../bson-binary-fromVector_error-003.phpt | 2 +- 7 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 src/BSON/VectorType.h diff --git a/src/BSON/Binary.c b/src/BSON/Binary.c index 20959bd06..d60cb792b 100644 --- a/src/BSON/Binary.c +++ b/src/BSON/Binary.c @@ -24,6 +24,7 @@ #include "phongo_error.h" #include "Binary.h" #include "Binary_arginfo.h" +#include "VectorType.h" zend_class_entry* php_phongo_binary_ce; @@ -45,7 +46,7 @@ static bool php_phongo_binary_init(php_phongo_binary_t* intern, const char* data return false; } - if ((type == BSON_SUBTYPE_VECTOR) && phongo_binary_get_vector_type_from_data((const uint8_t*) data, data_len) == PHONGO_BSON_VECTOR_TYPE_INVALID) { + if ((type == BSON_SUBTYPE_VECTOR) && phongo_binary_get_vector_type_from_data((const uint8_t*) data, data_len) == PHONGO_BSON_VECTOR_TYPE_UNKNOWN) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Binary vector data is invalid"); return false; } @@ -459,11 +460,11 @@ static void phongo_binary_init_vector_from_packed_bit_array(php_phongo_binary_t* ZEND_HASH_FOREACH_VAL_IND(vector, val) { if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) { - phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be an integer or boolean, %s given", i, zend_zval_type_name(val)); + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be 0, 1, or a boolean, %s given", i, zend_zval_type_name(val)); return; } - if (Z_TYPE_P(val) == IS_LONG && (Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > 1)) { + if (Z_TYPE_P(val) == IS_LONG && Z_LVAL_P(val) != 0 && Z_LVAL_P(val) != 1) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Expected vector[%zu] to be 0 or 1, %" PHONGO_LONG_FORMAT " given", i, Z_LVAL_P(val)); return; } @@ -484,8 +485,8 @@ static void phongo_binary_init_vector_from_packed_bit_array(php_phongo_binary_t* static PHP_METHOD(MongoDB_BSON_Binary, fromVector) { - HashTable* vector; - zend_object* type; + HashTable* vector; + zend_object* type; object_init_ex(return_value, php_phongo_binary_ce); php_phongo_binary_t* intern = Z_BINARY_OBJ_P(return_value); @@ -495,25 +496,20 @@ static PHP_METHOD(MongoDB_BSON_Binary, fromVector) Z_PARAM_OBJ_OF_CLASS(type, php_phongo_vectortype_ce) PHONGO_PARSE_PARAMETERS_END(); - zval *type_name = zend_enum_fetch_case_name(type); - - if (zend_string_equals_literal(Z_STR_P(type_name), "Float32")) { - phongo_binary_init_vector_from_float32_array(intern, vector); - return; - } - - if (zend_string_equals_literal(Z_STR_P(type_name), "Int8")) { - phongo_binary_init_vector_from_int8_array(intern, vector); - return; - } - - if (zend_string_equals_literal(Z_STR_P(type_name), "PackedBit")) { - phongo_binary_init_vector_from_packed_bit_array(intern, vector); - return; + switch (phongo_bson_vector_type_from_name(Z_STRVAL_P(zend_enum_fetch_case_name(type)))) { + case PHONGO_BSON_VECTOR_TYPE_FLOAT32: + phongo_binary_init_vector_from_float32_array(intern, vector); + return; + case PHONGO_BSON_VECTOR_TYPE_INT8: + phongo_binary_init_vector_from_int8_array(intern, vector); + return; + case PHONGO_BSON_VECTOR_TYPE_PACKED_BIT: + phongo_binary_init_vector_from_packed_bit_array(intern, vector); + return; + default: + phongo_throw_exception(PHONGO_ERROR_LOGIC, "Unsupported binary vector type: %s", Z_STRVAL_P(zend_enum_fetch_case_name(type))); + RETURN_THROWS(); } - - phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Unsupported binary vector type: %s", Z_STR_P(type_name)); - RETURN_THROWS(); } static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const uint8_t* data, uint32_t data_len) @@ -530,7 +526,7 @@ static phongo_bson_vector_type_t phongo_binary_get_vector_type_from_data(const u return PHONGO_BSON_VECTOR_TYPE_PACKED_BIT; } - return PHONGO_BSON_VECTOR_TYPE_INVALID; + return PHONGO_BSON_VECTOR_TYPE_UNKNOWN; } static phongo_bson_vector_type_t phongo_binary_get_vector_type(const php_phongo_binary_t* intern) @@ -549,23 +545,12 @@ static PHP_METHOD(MongoDB_BSON_Binary, getVectorType) RETURN_THROWS(); } - phongo_bson_vector_type_t type = phongo_binary_get_vector_type(Z_BINARY_OBJ_P(getThis())); - const char *type_case; + const char* type_case = phongo_bson_vector_type_to_name(phongo_binary_get_vector_type(Z_BINARY_OBJ_P(getThis()))); - switch (type) { - case PHONGO_BSON_VECTOR_TYPE_FLOAT32: - type_case = "Float32"; - break; - case PHONGO_BSON_VECTOR_TYPE_INT8: - type_case = "Int8"; - break; - case PHONGO_BSON_VECTOR_TYPE_PACKED_BIT: - type_case = "PackedBit"; - break; - default: - // The vector should always be valid by this point, but check for an error - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); - RETURN_THROWS(); + // The vector should always be valid by this point, but check for an error + if (!type_case) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); + RETURN_THROWS(); } RETVAL_OBJ_COPY(zend_enum_get_case_cstr(php_phongo_vectortype_ce, type_case)); @@ -576,7 +561,7 @@ static void phongo_binary_get_vector_as_array(const php_phongo_binary_t* intern, phongo_bson_vector_type_t type = phongo_binary_get_vector_type(intern); // The vector should always be valid by this point, but check for an error - if (type == PHONGO_BSON_VECTOR_TYPE_INVALID) { + if (type == PHONGO_BSON_VECTOR_TYPE_UNKNOWN) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); RETURN_THROWS(); } diff --git a/src/BSON/Binary.h b/src/BSON/Binary.h index 9b6ab4def..cdaa179ae 100644 --- a/src/BSON/Binary.h +++ b/src/BSON/Binary.h @@ -19,13 +19,6 @@ #define PHONGO_BINARY_UUID_SIZE 16 -typedef enum { - PHONGO_BSON_VECTOR_TYPE_INVALID = 0, - PHONGO_BSON_VECTOR_TYPE_INT8 = 0x03, - PHONGO_BSON_VECTOR_TYPE_FLOAT32 = 0x27, - PHONGO_BSON_VECTOR_TYPE_PACKED_BIT = 0x10, -} phongo_bson_vector_type_t; - bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type); #endif /* PHONGO_BSON_BINARY_H */ diff --git a/src/BSON/VectorType.c b/src/BSON/VectorType.c index d3ce5f85f..c74ec4874 100644 --- a/src/BSON/VectorType.c +++ b/src/BSON/VectorType.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-present MongoDB, Inc. + * Copyright 2025-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,42 @@ #include #include "php_phongo.h" +#include "VectorType.h" #include "VectorType_arginfo.h" zend_class_entry* php_phongo_vectortype_ce; +phongo_bson_vector_type_t phongo_bson_vector_type_from_name(const char* name) +{ + if (!strcmp(name, PHONGO_BSON_VECTOR_TYPE_FLOAT32_NAME)) { + return PHONGO_BSON_VECTOR_TYPE_FLOAT32; + } + + if (!strcmp(name, PHONGO_BSON_VECTOR_TYPE_INT8_NAME)) { + return PHONGO_BSON_VECTOR_TYPE_INT8; + } + + if (!strcmp(name, PHONGO_BSON_VECTOR_TYPE_PACKED_BIT_NAME)) { + return PHONGO_BSON_VECTOR_TYPE_PACKED_BIT; + } + + return PHONGO_BSON_VECTOR_TYPE_UNKNOWN; +} + +const char* phongo_bson_vector_type_to_name(phongo_bson_vector_type_t type) +{ + switch (type) { + case PHONGO_BSON_VECTOR_TYPE_FLOAT32: + return PHONGO_BSON_VECTOR_TYPE_FLOAT32_NAME; + case PHONGO_BSON_VECTOR_TYPE_INT8: + return PHONGO_BSON_VECTOR_TYPE_INT8_NAME; + case PHONGO_BSON_VECTOR_TYPE_PACKED_BIT: + return PHONGO_BSON_VECTOR_TYPE_PACKED_BIT_NAME; + default: + return NULL; + } +} + void php_phongo_vectortype_init_ce(INIT_FUNC_ARGS) { php_phongo_vectortype_ce = register_class_MongoDB_BSON_VectorType(); diff --git a/src/BSON/VectorType.h b/src/BSON/VectorType.h new file mode 100644 index 000000000..440d001ea --- /dev/null +++ b/src/BSON/VectorType.h @@ -0,0 +1,34 @@ +/* + * Copyright 2025-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_BSON_VECTORTYPE_H +#define PHONGO_BSON_VECTORTYPE_H + +#define PHONGO_BSON_VECTOR_TYPE_FLOAT32_NAME "Float32" +#define PHONGO_BSON_VECTOR_TYPE_INT8_NAME "Int8" +#define PHONGO_BSON_VECTOR_TYPE_PACKED_BIT_NAME "PackedBit" + +typedef enum { + PHONGO_BSON_VECTOR_TYPE_FLOAT32 = 0x27, + PHONGO_BSON_VECTOR_TYPE_INT8 = 0x03, + PHONGO_BSON_VECTOR_TYPE_PACKED_BIT = 0x10, + PHONGO_BSON_VECTOR_TYPE_UNKNOWN = 0, +} phongo_bson_vector_type_t; + +phongo_bson_vector_type_t phongo_bson_vector_type_from_name(const char* name); +const char* phongo_bson_vector_type_to_name(phongo_bson_vector_type_t type); + +#endif /* PHONGO_BSON_VECTORTYPE_H */ diff --git a/tests/bson/bson-binary-fromVector-001.phpt b/tests/bson/bson-binary-fromVector-001.phpt index d40d0ff54..5fd9cc8d0 100644 --- a/tests/bson/bson-binary-fromVector-001.phpt +++ b/tests/bson/bson-binary-fromVector-001.phpt @@ -4,7 +4,7 @@ MongoDB\BSON\Binary::fromVector() construction of various vector types @@ -38,13 +38,13 @@ object(MongoDB\BSON\Binary)#%d (%d) { ["vector"]=> array(4) { [0]=> - int(1) + int(-128) [1]=> - int(2) + int(0) [2]=> - int(3) + int(1) [3]=> - int(4) + int(127) } ["vectorType"]=> int(3) diff --git a/tests/bson/bson-binary-fromVector_error-002.phpt b/tests/bson/bson-binary-fromVector_error-002.phpt index 679a739d0..735a76930 100644 --- a/tests/bson/bson-binary-fromVector_error-002.phpt +++ b/tests/bson/bson-binary-fromVector_error-002.phpt @@ -14,7 +14,11 @@ echo throws(function() { }, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; echo throws(function() { - MongoDB\BSON\Binary::fromVector([1, 256], MongoDB\BSON\VectorType::Int8); + MongoDB\BSON\Binary::fromVector([1, -129], MongoDB\BSON\VectorType::Int8); +}, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; + +echo throws(function() { + MongoDB\BSON\Binary::fromVector([1, 128], MongoDB\BSON\VectorType::Int8); }, MongoDB\Driver\Exception\InvalidArgumentException::class), "\n"; ?> @@ -26,5 +30,7 @@ Expected vector to be a list OK: Got MongoDB\Driver\Exception\InvalidArgumentException Expected vector[1] to be an integer, float given OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Expected vector[1] to be a signed 8-bit integer, 256 given +Expected vector[1] to be a signed 8-bit integer, -129 given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected vector[1] to be a signed 8-bit integer, 128 given ===DONE=== diff --git a/tests/bson/bson-binary-fromVector_error-003.phpt b/tests/bson/bson-binary-fromVector_error-003.phpt index aee8b0256..f26e09be5 100644 --- a/tests/bson/bson-binary-fromVector_error-003.phpt +++ b/tests/bson/bson-binary-fromVector_error-003.phpt @@ -24,7 +24,7 @@ echo throws(function() { OK: Got MongoDB\Driver\Exception\InvalidArgumentException Expected vector to be a list OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Expected vector[1] to be an integer or boolean, float given +Expected vector[1] to be 0, 1, or a boolean, float given OK: Got MongoDB\Driver\Exception\InvalidArgumentException Expected vector[1] to be 0 or 1, 2 given ===DONE=== From 5fe1071f76e50c38512beb074a89fbe035bc0c7e Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 30 Sep 2025 17:32:49 -0400 Subject: [PATCH 3/5] Report vector type as enum in debug output --- src/BSON/Binary.c | 80 +++++++++++-------- src/BSON/VectorType.c | 16 ++++ src/BSON/VectorType.h | 2 + tests/bson/bson-binary-fromVector-001.phpt | 6 +- tests/bson/bson-binary-serialization-002.phpt | 4 +- 5 files changed, 69 insertions(+), 39 deletions(-) diff --git a/src/BSON/Binary.c b/src/BSON/Binary.c index d60cb792b..64fc4a90a 100644 --- a/src/BSON/Binary.c +++ b/src/BSON/Binary.c @@ -299,9 +299,16 @@ static HashTable* php_phongo_binary_get_debug_info(zend_object* object, int* is_ zend_hash_str_update(props, "vector", sizeof("vector") - 1, &vector); - zval vector_type; + zval vector_type; + zend_object* vector_type_case = phongo_bson_vector_type_to_case(phongo_binary_get_vector_type(intern)); - ZVAL_LONG(&vector_type, phongo_binary_get_vector_type(intern)); + // The vector should always be valid by this point, but check for an error + if (!vector_type_case) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); + return props; + } + + ZVAL_OBJ_COPY(&vector_type, vector_type_case); zend_hash_str_update(props, "vectorType", sizeof("vectorType") - 1, &vector_type); } @@ -496,7 +503,7 @@ static PHP_METHOD(MongoDB_BSON_Binary, fromVector) Z_PARAM_OBJ_OF_CLASS(type, php_phongo_vectortype_ce) PHONGO_PARSE_PARAMETERS_END(); - switch (phongo_bson_vector_type_from_name(Z_STRVAL_P(zend_enum_fetch_case_name(type)))) { + switch (phongo_bson_vector_type_from_case(type)) { case PHONGO_BSON_VECTOR_TYPE_FLOAT32: phongo_binary_init_vector_from_float32_array(intern, vector); return; @@ -545,7 +552,7 @@ static PHP_METHOD(MongoDB_BSON_Binary, getVectorType) RETURN_THROWS(); } - const char* type_case = phongo_bson_vector_type_to_name(phongo_binary_get_vector_type(Z_BINARY_OBJ_P(getThis()))); + const char* type_case = phongo_bson_vector_type_to_name(phongo_binary_get_vector_type(intern)); // The vector should always be valid by this point, but check for an error if (!type_case) { @@ -558,43 +565,48 @@ static PHP_METHOD(MongoDB_BSON_Binary, getVectorType) static void phongo_binary_get_vector_as_array(const php_phongo_binary_t* intern, zval* return_value) { - phongo_bson_vector_type_t type = phongo_binary_get_vector_type(intern); - - // The vector should always be valid by this point, but check for an error - if (type == PHONGO_BSON_VECTOR_TYPE_UNKNOWN) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); - RETURN_THROWS(); - } - bson_t tmp_doc = BSON_INITIALIZER; - if (type == PHONGO_BSON_VECTOR_TYPE_INT8) { - bson_vector_int8_const_view_t view; + switch (phongo_binary_get_vector_type(intern)) { + case PHONGO_BSON_VECTOR_TYPE_INT8: { + bson_vector_int8_const_view_t view; - if (!bson_vector_int8_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || - !BSON_APPEND_ARRAY_FROM_VECTOR_INT8(&tmp_doc, "vector", view)) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); - bson_destroy(&tmp_doc); - RETURN_THROWS(); + if (!bson_vector_int8_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_INT8(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + + break; } - } else if (type == PHONGO_BSON_VECTOR_TYPE_FLOAT32) { - bson_vector_float32_const_view_t view; + case PHONGO_BSON_VECTOR_TYPE_FLOAT32: { + bson_vector_float32_const_view_t view; - if (!bson_vector_float32_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || - !BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(&tmp_doc, "vector", view)) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); - bson_destroy(&tmp_doc); - RETURN_THROWS(); + if (!bson_vector_float32_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + + break; } - } else if (type == PHONGO_BSON_VECTOR_TYPE_PACKED_BIT) { - bson_vector_packed_bit_const_view_t view; + case PHONGO_BSON_VECTOR_TYPE_PACKED_BIT: { + bson_vector_packed_bit_const_view_t view; - if (!bson_vector_packed_bit_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || - !BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(&tmp_doc, "vector", view)) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); - bson_destroy(&tmp_doc); - RETURN_THROWS(); + if (!bson_vector_packed_bit_const_view_init(&view, (const uint8_t*) intern->data, intern->data_len) || + !BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(&tmp_doc, "vector", view)) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to convert binary vector data to an array"); + bson_destroy(&tmp_doc); + RETURN_THROWS(); + } + + break; } + default: + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Binary vector data is invalid"); + RETURN_THROWS(); } bson_iter_t iter; @@ -647,5 +659,5 @@ static PHP_METHOD(MongoDB_BSON_Binary, toArray) RETURN_THROWS(); } - phongo_binary_get_vector_as_array(Z_BINARY_OBJ_P(getThis()), return_value); + phongo_binary_get_vector_as_array(intern, return_value); } diff --git a/src/BSON/VectorType.c b/src/BSON/VectorType.c index c74ec4874..8b9fdae18 100644 --- a/src/BSON/VectorType.c +++ b/src/BSON/VectorType.c @@ -54,6 +54,22 @@ const char* phongo_bson_vector_type_to_name(phongo_bson_vector_type_t type) } } +phongo_bson_vector_type_t phongo_bson_vector_type_from_case(zend_object* case_obj) +{ + return phongo_bson_vector_type_from_name(Z_STRVAL_P(zend_enum_fetch_case_name(case_obj))); +} + +zend_object* phongo_bson_vector_type_to_case(phongo_bson_vector_type_t type) +{ + const char* name = phongo_bson_vector_type_to_name(type); + + if (!name) { + return NULL; + } + + return zend_enum_get_case_cstr(php_phongo_vectortype_ce, name); +} + void php_phongo_vectortype_init_ce(INIT_FUNC_ARGS) { php_phongo_vectortype_ce = register_class_MongoDB_BSON_VectorType(); diff --git a/src/BSON/VectorType.h b/src/BSON/VectorType.h index 440d001ea..78d096d28 100644 --- a/src/BSON/VectorType.h +++ b/src/BSON/VectorType.h @@ -30,5 +30,7 @@ typedef enum { phongo_bson_vector_type_t phongo_bson_vector_type_from_name(const char* name); const char* phongo_bson_vector_type_to_name(phongo_bson_vector_type_t type); +phongo_bson_vector_type_t phongo_bson_vector_type_from_case(zend_object* case_obj); +zend_object* phongo_bson_vector_type_to_case(phongo_bson_vector_type_t type); #endif /* PHONGO_BSON_VECTORTYPE_H */ diff --git a/tests/bson/bson-binary-fromVector-001.phpt b/tests/bson/bson-binary-fromVector-001.phpt index 5fd9cc8d0..2ccd522a4 100644 --- a/tests/bson/bson-binary-fromVector-001.phpt +++ b/tests/bson/bson-binary-fromVector-001.phpt @@ -28,7 +28,7 @@ object(MongoDB\BSON\Binary)#%d (%d) { float(-0.5) } ["vectorType"]=> - int(39) + enum(MongoDB\BSON\VectorType::Float32) } object(MongoDB\BSON\Binary)#%d (%d) { ["data"]=> @@ -47,7 +47,7 @@ object(MongoDB\BSON\Binary)#%d (%d) { int(127) } ["vectorType"]=> - int(3) + enum(MongoDB\BSON\VectorType::Int8) } object(MongoDB\BSON\Binary)#%d (%d) { ["data"]=> @@ -66,6 +66,6 @@ object(MongoDB\BSON\Binary)#%d (%d) { int(0) } ["vectorType"]=> - int(16) + enum(MongoDB\BSON\VectorType::PackedBit) } ===DONE=== diff --git a/tests/bson/bson-binary-serialization-002.phpt b/tests/bson/bson-binary-serialization-002.phpt index 61277e476..80f4b3212 100644 --- a/tests/bson/bson-binary-serialization-002.phpt +++ b/tests/bson/bson-binary-serialization-002.phpt @@ -112,7 +112,7 @@ object(MongoDB\BSON\Binary)#%d (%d) { int(4) } ["vectorType"]=> - int(3) + enum(MongoDB\BSON\VectorType::Int8) } string(70) "O:19:"MongoDB\BSON\Binary":2:{s:4:"data";s:6:"%a";s:4:"type";i:9;}" object(MongoDB\BSON\Binary)#%d (%d) { @@ -132,7 +132,7 @@ object(MongoDB\BSON\Binary)#%d (%d) { int(4) } ["vectorType"]=> - int(3) + enum(MongoDB\BSON\VectorType::Int8) } ===DONE=== From 9d25c3cf517876a714f4e9b40a11e0588912a650 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 2 Oct 2025 21:46:43 -0400 Subject: [PATCH 4/5] Regenerate stubs with PHP 8.2 --- src/BSON/Binary_arginfo.h | 5 ++++- src/BSON/VectorType_arginfo.h | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/BSON/Binary_arginfo.h b/src/BSON/Binary_arginfo.h index 7471b154e..832e42e90 100644 --- a/src/BSON/Binary_arginfo.h +++ b/src/BSON/Binary_arginfo.h @@ -38,6 +38,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_BSON_Binary_jsonSerialize, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() + static ZEND_METHOD(MongoDB_BSON_Binary, __construct); static ZEND_METHOD(MongoDB_BSON_Binary, fromVector); static ZEND_METHOD(MongoDB_BSON_Binary, getData); @@ -50,6 +51,7 @@ static ZEND_METHOD(MongoDB_BSON_Binary, __unserialize); static ZEND_METHOD(MongoDB_BSON_Binary, __serialize); static ZEND_METHOD(MongoDB_BSON_Binary, jsonSerialize); + static const zend_function_entry class_MongoDB_BSON_Binary_methods[] = { ZEND_ME(MongoDB_BSON_Binary, __construct, arginfo_class_MongoDB_BSON_Binary___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_BSON_Binary, fromVector, arginfo_class_MongoDB_BSON_Binary_fromVector, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) @@ -70,7 +72,8 @@ static zend_class_entry *register_class_MongoDB_BSON_Binary(zend_class_entry *cl zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY(ce, "MongoDB\\BSON", "Binary", class_MongoDB_BSON_Binary_methods); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; zend_class_implements(class_entry, 4, class_entry_MongoDB_BSON_BinaryInterface, class_entry_JsonSerializable, class_entry_MongoDB_BSON_Type, class_entry_Stringable); zval const_TYPE_GENERIC_value; diff --git a/src/BSON/VectorType_arginfo.h b/src/BSON/VectorType_arginfo.h index 22cc6073d..5cb3b84a0 100644 --- a/src/BSON/VectorType_arginfo.h +++ b/src/BSON/VectorType_arginfo.h @@ -1,9 +1,16 @@ /* This is a generated file, edit the .stub.php file instead. * Stub hash: 9fc1377046ca92a6a50cfac11fabc50b4d30d708 */ + + + +static const zend_function_entry class_MongoDB_BSON_VectorType_methods[] = { + ZEND_FE_END +}; + static zend_class_entry *register_class_MongoDB_BSON_VectorType(void) { - zend_class_entry *class_entry = zend_register_internal_enum("MongoDB\\BSON\\VectorType", IS_UNDEF, NULL); + zend_class_entry *class_entry = zend_register_internal_enum("MongoDB\\BSON\\VectorType", IS_UNDEF, class_MongoDB_BSON_VectorType_methods); zend_enum_add_case_cstr(class_entry, "Float32", NULL); From 6bd8a44f4333071a8f5d255aca2971ffcafa8f05 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Mon, 6 Oct 2025 23:21:39 -0400 Subject: [PATCH 5/5] Revise vector tests --- .../bson-binary-serialization_error-007.phpt | 18 ++++++++++++++++++ tests/bson/bson-vectortype-001.phpt | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/bson/bson-binary-serialization_error-007.phpt diff --git a/tests/bson/bson-binary-serialization_error-007.phpt b/tests/bson/bson-binary-serialization_error-007.phpt new file mode 100644 index 000000000..eba1b20da --- /dev/null +++ b/tests/bson/bson-binary-serialization_error-007.phpt @@ -0,0 +1,18 @@ +--TEST-- +MongoDB\BSON\Binary unserialization requires valid vector data (__serialize and __unserialize) +--FILE-- + +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Binary vector data is invalid +===DONE=== diff --git a/tests/bson/bson-vectortype-001.phpt b/tests/bson/bson-vectortype-001.phpt index 1feb22094..c72dd2cf4 100644 --- a/tests/bson/bson-vectortype-001.phpt +++ b/tests/bson/bson-vectortype-001.phpt @@ -1,11 +1,11 @@ --TEST-- -MongoDB\BSON\VectorType +MongoDB\BSON\VectorType cases --FILE-- ===DONE===