From dc2681d90a9534403bb61ce9ee81df6c710d812a Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 31 Jul 2023 10:35:28 +0200 Subject: [PATCH 01/10] Document and PackedArray classes implement Type interface --- src/BSON/Document.c | 2 +- src/BSON/Document.stub.php | 2 +- src/BSON/Document_arginfo.h | 6 +++--- src/BSON/PackedArray.c | 2 +- src/BSON/PackedArray.stub.php | 2 +- src/BSON/PackedArray_arginfo.h | 6 +++--- src/phongo_bson_encode.c | 22 +++++++++++----------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/BSON/Document.c b/src/BSON/Document.c index e53292995..e92652eb4 100644 --- a/src/BSON/Document.c +++ b/src/BSON/Document.c @@ -473,7 +473,7 @@ static HashTable* php_phongo_document_get_properties(phongo_compat_object_handle void php_phongo_document_init_ce(INIT_FUNC_ARGS) { - php_phongo_document_ce = register_class_MongoDB_BSON_Document(zend_ce_aggregate, zend_ce_serializable); + php_phongo_document_ce = register_class_MongoDB_BSON_Document(zend_ce_aggregate, zend_ce_serializable, php_phongo_type_ce); php_phongo_document_ce->create_object = php_phongo_document_create_object; #if PHP_VERSION_ID >= 80000 diff --git a/src/BSON/Document.stub.php b/src/BSON/Document.stub.php index 1c8f46dc7..20282d708 100644 --- a/src/BSON/Document.stub.php +++ b/src/BSON/Document.stub.php @@ -7,7 +7,7 @@ namespace MongoDB\BSON; -final class Document implements \IteratorAggregate, \Serializable +final class Document implements \IteratorAggregate, \Serializable, Type { private function __construct() {} diff --git a/src/BSON/Document_arginfo.h b/src/BSON/Document_arginfo.h index c460bbea5..c3632f7db 100644 --- a/src/BSON/Document_arginfo.h +++ b/src/BSON/Document_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e00ccf66afed0f51040527ee5ee24513f0c1f495 */ + * Stub hash: 703e15f17b01dd2b6f04cb89c080ba83a5a420d0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_BSON_Document___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -166,14 +166,14 @@ static const zend_function_entry class_MongoDB_BSON_Document_methods[] = { ZEND_FE_END }; -static zend_class_entry *register_class_MongoDB_BSON_Document(zend_class_entry *class_entry_IteratorAggregate, zend_class_entry *class_entry_Serializable) +static zend_class_entry *register_class_MongoDB_BSON_Document(zend_class_entry *class_entry_IteratorAggregate, zend_class_entry *class_entry_Serializable, zend_class_entry *class_entry_MongoDB_BSON_Type) { zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY(ce, "MongoDB\\BSON", "Document", class_MongoDB_BSON_Document_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; - zend_class_implements(class_entry, 2, class_entry_IteratorAggregate, class_entry_Serializable); + zend_class_implements(class_entry, 3, class_entry_IteratorAggregate, class_entry_Serializable, class_entry_MongoDB_BSON_Type); return class_entry; } diff --git a/src/BSON/PackedArray.c b/src/BSON/PackedArray.c index 35d2818ed..22d03dd5c 100644 --- a/src/BSON/PackedArray.c +++ b/src/BSON/PackedArray.c @@ -403,7 +403,7 @@ static HashTable* php_phongo_packedarray_get_properties(phongo_compat_object_han void php_phongo_packedarray_init_ce(INIT_FUNC_ARGS) { - php_phongo_packedarray_ce = register_class_MongoDB_BSON_PackedArray(zend_ce_aggregate, zend_ce_serializable); + php_phongo_packedarray_ce = register_class_MongoDB_BSON_PackedArray(zend_ce_aggregate, zend_ce_serializable, php_phongo_type_ce); php_phongo_packedarray_ce->create_object = php_phongo_packedarray_create_object; #if PHP_VERSION_ID >= 80000 diff --git a/src/BSON/PackedArray.stub.php b/src/BSON/PackedArray.stub.php index 33f6c4614..a6d681a4e 100644 --- a/src/BSON/PackedArray.stub.php +++ b/src/BSON/PackedArray.stub.php @@ -7,7 +7,7 @@ namespace MongoDB\BSON; -final class PackedArray implements \IteratorAggregate, \Serializable +final class PackedArray implements \IteratorAggregate, \Serializable, Type { private function __construct() {} diff --git a/src/BSON/PackedArray_arginfo.h b/src/BSON/PackedArray_arginfo.h index 61f50c22e..3d7727329 100644 --- a/src/BSON/PackedArray_arginfo.h +++ b/src/BSON/PackedArray_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e9b57c4051440170531560cc355e0af1b43f3424 */ + * Stub hash: f9813432d7811e068f62a4b34d86919a52f63984 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_BSON_PackedArray___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -128,14 +128,14 @@ static const zend_function_entry class_MongoDB_BSON_PackedArray_methods[] = { ZEND_FE_END }; -static zend_class_entry *register_class_MongoDB_BSON_PackedArray(zend_class_entry *class_entry_IteratorAggregate, zend_class_entry *class_entry_Serializable) +static zend_class_entry *register_class_MongoDB_BSON_PackedArray(zend_class_entry *class_entry_IteratorAggregate, zend_class_entry *class_entry_Serializable, zend_class_entry *class_entry_MongoDB_BSON_Type) { zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY(ce, "MongoDB\\BSON", "PackedArray", class_MongoDB_BSON_PackedArray_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; - zend_class_implements(class_entry, 2, class_entry_IteratorAggregate, class_entry_Serializable); + zend_class_implements(class_entry, 3, class_entry_IteratorAggregate, class_entry_Serializable, class_entry_MongoDB_BSON_Type); return class_entry; } diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index 1dcc92566..d51bf09b1 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -103,19 +103,19 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f return; } - if (Z_TYPE_P(object) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce) || instanceof_function(Z_OBJCE_P(object), php_phongo_packedarray_ce))) { - if (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce)) { - php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(object); - bson_append_document(bson, key, key_len, intern->bson); - } else { - php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(object); - bson_append_array(bson, key, key_len, intern->bson); - } + if (Z_TYPE_P(object) == IS_OBJECT && instanceof_function(Z_OBJCE_P(object), php_phongo_type_ce)) { + if (Z_TYPE_P(object) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce) || instanceof_function(Z_OBJCE_P(object), php_phongo_packedarray_ce))) { + if (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce)) { + php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(object); + bson_append_document(bson, key, key_len, intern->bson); + } else { + php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(object); + bson_append_array(bson, key, key_len, intern->bson); + } - return; - } + return; + } - if (Z_TYPE_P(object) == IS_OBJECT && instanceof_function(Z_OBJCE_P(object), php_phongo_type_ce)) { if (instanceof_function(Z_OBJCE_P(object), php_phongo_serializable_ce)) { zval obj_data; bson_t child; From a2603e4e93bbb586cd956fb0dd4a70b7cf44f3f9 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 31 Jul 2023 10:41:10 +0200 Subject: [PATCH 02/10] Narrow return type for Serializable::bsonSerialize The previously indicated return type was incorrect, as returning an object other than stdClass would result in an error. --- src/BSON/Serializable.stub.php | 4 ++-- src/BSON/Serializable_arginfo.h | 4 ++-- src/MongoDB/ReadConcern.stub.php | 4 ++-- src/MongoDB/ReadConcern_arginfo.h | 4 ++-- src/MongoDB/ReadPreference.stub.php | 4 ++-- src/MongoDB/ReadPreference_arginfo.h | 4 ++-- src/MongoDB/ServerApi.stub.php | 4 ++-- src/MongoDB/ServerApi_arginfo.h | 4 ++-- src/MongoDB/WriteConcern.stub.php | 4 ++-- src/MongoDB/WriteConcern_arginfo.h | 4 ++-- tests/bson/bson-document-fromPHP-003.phpt | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/BSON/Serializable.stub.php b/src/BSON/Serializable.stub.php index 78067a262..ef0d8936a 100644 --- a/src/BSON/Serializable.stub.php +++ b/src/BSON/Serializable.stub.php @@ -11,9 +11,9 @@ interface Serializable extends Type { #if PHP_VERSION_ID >= 80000 /** @tentative-return-type */ - public function bsonSerialize(): array|object; + public function bsonSerialize(): array|\stdClass; #else - /** @return array|object */ + /** @return array|\stdClass */ public function bsonSerialize(); #endif } diff --git a/src/BSON/Serializable_arginfo.h b/src/BSON/Serializable_arginfo.h index 106e603dc..1945d8446 100644 --- a/src/BSON/Serializable_arginfo.h +++ b/src/BSON/Serializable_arginfo.h @@ -1,8 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cbb517e1d922625ff18bdc81620686a44a87e85c */ + * Stub hash: cc59819a80929f30a89bde0ded725ee4de9d37f5 */ #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_MongoDB_BSON_Serializable_bsonSerialize, 0, 0, MAY_BE_ARRAY|MAY_BE_OBJECT) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_BSON_Serializable_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/src/MongoDB/ReadConcern.stub.php b/src/MongoDB/ReadConcern.stub.php index b75999841..91c4d4de8 100644 --- a/src/MongoDB/ReadConcern.stub.php +++ b/src/MongoDB/ReadConcern.stub.php @@ -48,9 +48,9 @@ final public function isDefault(): bool {} final public static function __set_state(array $properties): ReadConcern {} #if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|object {} + final public function bsonSerialize(): array|\stdClass {} #else - /** @return array|object */ + /** @return array|\stdClass */ final public function bsonSerialize() {} #endif diff --git a/src/MongoDB/ReadConcern_arginfo.h b/src/MongoDB/ReadConcern_arginfo.h index 54b10f1d8..09145f2d6 100644 --- a/src/MongoDB/ReadConcern_arginfo.h +++ b/src/MongoDB/ReadConcern_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cc214d2fd99e6191747053214210d07da48831a7 */ + * Stub hash: b8616bced8b4b9fcc10eb931a081c4476ff5d134 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, level, IS_STRING, 1, "null") @@ -16,7 +16,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern_ ZEND_END_ARG_INFO() #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, 0, 0, MAY_BE_ARRAY|MAY_BE_OBJECT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/src/MongoDB/ReadPreference.stub.php b/src/MongoDB/ReadPreference.stub.php index 740baf060..6cabfd5fe 100644 --- a/src/MongoDB/ReadPreference.stub.php +++ b/src/MongoDB/ReadPreference.stub.php @@ -101,9 +101,9 @@ final public function getTagSets(): array {} final public static function __set_state(array $properties): ReadPreference {} #if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|object {} + final public function bsonSerialize(): array|\stdClass {} #else - /** @return array|object */ + /** @return array|\stdClass */ final public function bsonSerialize() {} #endif diff --git a/src/MongoDB/ReadPreference_arginfo.h b/src/MongoDB/ReadPreference_arginfo.h index 13b9579cd..05791b27c 100644 --- a/src/MongoDB/ReadPreference_arginfo.h +++ b/src/MongoDB/ReadPreference_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: df8b13e252c05cbb7603dec8c1659fb88845ed73 */ + * Stub hash: 9868e4e3be9c2df920f996b7b881c67e2d46e6ea */ #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreference___construct, 0, 0, 1) @@ -36,7 +36,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreferen ZEND_END_ARG_INFO() #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, 0, 0, MAY_BE_ARRAY|MAY_BE_OBJECT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/src/MongoDB/ServerApi.stub.php b/src/MongoDB/ServerApi.stub.php index d4c21eced..6a99e6976 100644 --- a/src/MongoDB/ServerApi.stub.php +++ b/src/MongoDB/ServerApi.stub.php @@ -17,9 +17,9 @@ final public function __construct(string $version, ?bool $strict = null, ?bool $ final public static function __set_state(array $properties): ServerApi {} #if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|object {} + final public function bsonSerialize(): array|\stdClass {} #else - /** @return array|object */ + /** @return array|\stdClass */ final public function bsonSerialize() {} #endif diff --git a/src/MongoDB/ServerApi_arginfo.h b/src/MongoDB/ServerApi_arginfo.h index 402064b15..4967f9647 100644 --- a/src/MongoDB/ServerApi_arginfo.h +++ b/src/MongoDB/ServerApi_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 27db2a958d63eaf3ca813416bf0756c981dd013f */ + * Stub hash: 50ac33dea8943336b9c562a90e189036c90f1479 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, version, IS_STRING, 0) @@ -12,7 +12,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi___ ZEND_END_ARG_INFO() #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, 0, 0, MAY_BE_ARRAY|MAY_BE_OBJECT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/src/MongoDB/WriteConcern.stub.php b/src/MongoDB/WriteConcern.stub.php index 257dc65b1..afd212de4 100644 --- a/src/MongoDB/WriteConcern.stub.php +++ b/src/MongoDB/WriteConcern.stub.php @@ -38,9 +38,9 @@ final public function isDefault(): bool {} final public static function __set_state(array $properties): WriteConcern {} #if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|object {} + final public function bsonSerialize(): array|\stdClass {} #else - /** @return array|object */ + /** @return array|\stdClass */ final public function bsonSerialize() {} #endif diff --git a/src/MongoDB/WriteConcern_arginfo.h b/src/MongoDB/WriteConcern_arginfo.h index 1ec27afbb..68a99a6df 100644 --- a/src/MongoDB/WriteConcern_arginfo.h +++ b/src/MongoDB/WriteConcern_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 071d716b692ad1d3abdaf61bc96e9671f64f71e1 */ + * Stub hash: bc052f233ec885eb7209a77d32fd9290a2a101e3 */ #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern___construct, 0, 0, 1) @@ -41,7 +41,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern ZEND_END_ARG_INFO() #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, 0, 0, MAY_BE_ARRAY|MAY_BE_OBJECT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/tests/bson/bson-document-fromPHP-003.phpt b/tests/bson/bson-document-fromPHP-003.phpt index 138a9cff7..b3196646d 100644 --- a/tests/bson/bson-document-fromPHP-003.phpt +++ b/tests/bson/bson-document-fromPHP-003.phpt @@ -10,7 +10,7 @@ class MyDocument implements MongoDB\BSON\Persistable { protected $bar = 2; public $baz = 3; - public function bsonSerialize(): object + public function bsonSerialize(): stdClass { return (object) [ 'foo' => $this->foo, From 251f4acf346f3504a54d40da48852cf7e92a85d1 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 31 Jul 2023 13:08:02 +0200 Subject: [PATCH 03/10] Add compat macro for ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX --- src/phongo_compat.h | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/phongo_compat.h b/src/phongo_compat.h index 88939f449..976ac33c9 100644 --- a/src/phongo_compat.h +++ b/src/phongo_compat.h @@ -285,15 +285,6 @@ static inline zend_bool zend_ini_parse_bool(zend_string* str) Z_PARAM_ARRAY_OR_OBJECT(dest) #endif -/* Per https://wiki.php.net/rfc/internal_method_return_types, "Non-final - * internal method return types - when possible - are declared tentatively in - * PHP 8.1, and they will become enforced in PHP 9.0." This can be revisited - * when more general typing improvements are made in PHPC-1709. */ -#ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX -#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ - ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) -#endif - /* ZEND_ABSTRACT_ME_WITH_FLAGS was introduced in PHP 8.0. */ #ifndef ZEND_ABSTRACT_ME_WITH_FLAGS #define ZEND_ABSTRACT_ME_WITH_FLAGS(classname, name, arg_info, flags) ZEND_RAW_FENTRY(#name, NULL, arg_info, flags) @@ -322,16 +313,22 @@ static inline zend_bool zend_ini_parse_bool(zend_string* str) #define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) #endif - +#ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ + ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) +#endif #ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX #define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) #endif - #ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX #define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) #endif +#ifndef ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX +#define ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \ + ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args) +#endif zend_bool php_phongo_zend_hash_apply_protection_begin(HashTable* ht); zend_bool php_phongo_zend_hash_apply_protection_end(HashTable* ht); From 76a13cba8e0e77c0533b5d1dc078adf49a5b156d Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 31 Jul 2023 11:54:30 +0200 Subject: [PATCH 04/10] PHPC-1000: Allow bsonSerialize to return BSON structures --- src/BSON/Serializable.stub.php | 4 +-- src/BSON/Serializable_arginfo.h | 4 +-- src/phongo_bson_encode.c | 37 ++++++++++++++++++-------- tests/bson/bson-fromPHP-001.phpt | 30 ++++++++++++++++++++- tests/bson/bson-fromPHP_error-001.phpt | 20 +++++++------- tests/bson/bson-fromPHP_error-005.phpt | 2 +- 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/BSON/Serializable.stub.php b/src/BSON/Serializable.stub.php index ef0d8936a..44a45d311 100644 --- a/src/BSON/Serializable.stub.php +++ b/src/BSON/Serializable.stub.php @@ -11,9 +11,9 @@ interface Serializable extends Type { #if PHP_VERSION_ID >= 80000 /** @tentative-return-type */ - public function bsonSerialize(): array|\stdClass; + public function bsonSerialize(): array|\stdClass|Document|PackedArray; #else - /** @return array|\stdClass */ + /** @return array|\stdClass|Document|PackedArray */ public function bsonSerialize(); #endif } diff --git a/src/BSON/Serializable_arginfo.h b/src/BSON/Serializable_arginfo.h index 1945d8446..a2039eb94 100644 --- a/src/BSON/Serializable_arginfo.h +++ b/src/BSON/Serializable_arginfo.h @@ -1,8 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cc59819a80929f30a89bde0ded725ee4de9d37f5 */ + * Stub hash: 92732bafa65af8a12b291f7bcb52b069c45724a7 */ #if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_BSON_Serializable_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_BSON_Serializable_bsonSerialize, 0, 0, stdClass|MongoDB\\BSON\\Document|MongoDB\\BSON\\PackedArray, MAY_BE_ARRAY) ZEND_END_ARG_INFO() #endif diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index d51bf09b1..0b20801f1 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -119,6 +119,7 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f if (instanceof_function(Z_OBJCE_P(object), php_phongo_serializable_ce)) { zval obj_data; bson_t child; + bool is_array; zend_call_method_with_0_params(PHONGO_COMPAT_OBJ_P(object), NULL, NULL, BSON_SERIALIZE_FUNC_NAME, &obj_data); @@ -128,20 +129,25 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f return; } - if (Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def))) { + if ( + Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array or stdClass, %s given", + "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", ZSTR_VAL(Z_OBJCE_P(object)->name), BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + ZSTR_VAL(php_phongo_packedarray_ce->name), PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); zval_ptr_dtor(&obj_data); return; } + is_array = php_phongo_is_array_or_document(&obj_data) == IS_ARRAY || (Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)); + /* Persistable objects must always be serialized as BSON documents; * otherwise, infer based on bsonSerialize()'s return value. */ - if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce) || php_phongo_is_array_or_document(&obj_data) == IS_OBJECT) { + if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce) || !is_array) { bson_append_document_begin(bson, key, key_len, &child); if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce)) { bson_append_binary(&child, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(object)->name->val, Z_OBJCE_P(object)->name->len); @@ -451,12 +457,28 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* return; } + if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { + bson_append_binary(bson, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(data)->name->val, Z_OBJCE_P(data)->name->len); + /* Ensure that we ignore an existing key with the same name + * if one exists in the bsonSerialize() return value. */ + skip_odm_field = true; + } + + // If bsonSerialize() returns a BSON document or packedArray instance, recurse to copy data over directly + if (Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce))) { + php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, bson, bson_out); + + goto done; + } + if (Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def))) { phongo_throw_exception( PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array or stdClass, %s given", + "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", ZSTR_VAL(Z_OBJCE_P(data)->name), BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + ZSTR_VAL(php_phongo_packedarray_ce->name), PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); goto cleanup; @@ -464,13 +486,6 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* ht_data = HASH_OF(&obj_data); - if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { - bson_append_binary(bson, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(data)->name->val, Z_OBJCE_P(data)->name->len); - /* Ensure that we ignore an existing key with the same name - * if one exists in the bsonSerialize() return value. */ - skip_odm_field = true; - } - break; } diff --git a/tests/bson/bson-fromPHP-001.phpt b/tests/bson/bson-fromPHP-001.phpt index 3a763cff0..b104fa272 100644 --- a/tests/bson/bson-fromPHP-001.phpt +++ b/tests/bson/bson-fromPHP-001.phpt @@ -1,5 +1,5 @@ --TEST-- -MongoDB\BSON\fromPHP(): bsonSerialize() allows array and stdClass +MongoDB\BSON\fromPHP(): bsonSerialize() allows arrays, stdClass instances, BSON arrays, and BSON documents --FILE-- data = $data; + } +} + $tests = array( array(1, 2, 3), array('foo' => 'bar'), (object) array(1, 2, 3), (object) array('foo' => 'bar'), + MongoDB\BSON\PackedArray::fromPHP([1, 2, 3]), + MongoDB\BSON\Document::fromPHP(['foo' => 'bar']), ); echo "Testing top-level objects\n"; @@ -33,6 +43,7 @@ echo "Testing top-level objects\n"; foreach ($tests as $test) { try { echo toJson(fromPHP(new MyDocument($test))), "\n"; + echo toJson(fromPHP(new MyPersistableDocument($test))), "\n"; } catch (MongoDB\Driver\Exception\UnexpectedValueException $e) { echo $e->getMessage(), "\n"; } @@ -43,6 +54,7 @@ echo "\nTesting nested objects\n"; foreach ($tests as $test) { try { echo toJson(fromPHP(new MyDocument(array('nested' => new MyDocument($test))))), "\n"; + echo toJson(fromPHP(new MyDocument(array('nested' => new MyPersistableDocument($test))))), "\n"; } catch (MongoDB\Driver\Exception\UnexpectedValueException $e) { echo $e->getMessage(), "\n"; } @@ -54,13 +66,29 @@ foreach ($tests as $test) { --EXPECT-- Testing top-level objects { "0" : 1, "1" : 2, "2" : 3 } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } +{ "foo" : "bar" } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } +{ "0" : 1, "1" : 2, "2" : 3 } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } { "foo" : "bar" } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } { "0" : 1, "1" : 2, "2" : 3 } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } { "foo" : "bar" } +{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } Testing nested objects { "nested" : [ 1, 2, 3 ] } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } } { "nested" : { "foo" : "bar" } } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } } { "nested" : { "0" : 1, "1" : 2, "2" : 3 } } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } } +{ "nested" : { "foo" : "bar" } } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } } +{ "nested" : [ 1, 2, 3 ] } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } } { "nested" : { "foo" : "bar" } } +{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } } ===DONE=== diff --git a/tests/bson/bson-fromPHP_error-001.phpt b/tests/bson/bson-fromPHP_error-001.phpt index d39ee7baa..6e722b7cb 100644 --- a/tests/bson/bson-fromPHP_error-001.phpt +++ b/tests/bson/bson-fromPHP_error-001.phpt @@ -48,16 +48,16 @@ foreach ($invalidValues as $invalidValue) { --EXPECTF-- Testing top-level objects -Expected MyDocument::bsonSerialize() to return an array or stdClass, %r(null|NULL)%r given -Expected MyDocument::bsonSerialize() to return an array or stdClass, int%S given -Expected MyDocument::bsonSerialize() to return an array or stdClass, string given -Expected MyDocument::bsonSerialize() to return an array or stdClass, bool%S given -Expected MyDocument::bsonSerialize() to return an array or stdClass, MyDocument given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, %r(null|NULL)%r given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, int%S given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, string given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, bool%S given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, MyDocument given Testing nested objects -Expected MyDocument::bsonSerialize() to return an array or stdClass, %r(null|NULL)%r given -Expected MyDocument::bsonSerialize() to return an array or stdClass, int%S given -Expected MyDocument::bsonSerialize() to return an array or stdClass, string given -Expected MyDocument::bsonSerialize() to return an array or stdClass, bool%S given -Expected MyDocument::bsonSerialize() to return an array or stdClass, MyDocument given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, %r(null|NULL)%r given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, int%S given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, string given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, bool%S given +Expected MyDocument::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, MyDocument given ===DONE=== diff --git a/tests/bson/bson-fromPHP_error-005.phpt b/tests/bson/bson-fromPHP_error-005.phpt index 67668270f..d7fea4d72 100644 --- a/tests/bson/bson-fromPHP_error-005.phpt +++ b/tests/bson/bson-fromPHP_error-005.phpt @@ -43,7 +43,7 @@ echo throws(function() { --EXPECT-- Testing Serializable with direct circular reference OK: Got MongoDB\Driver\Exception\UnexpectedValueException -Expected MyRecursiveSerializable::bsonSerialize() to return an array or stdClass, MyRecursiveSerializable given +Expected MyRecursiveSerializable::bsonSerialize() to return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray, MyRecursiveSerializable given Testing Serializable with indirect circular reference OK: Got MongoDB\Driver\Exception\UnexpectedValueException From fc928f5d162ef8fce1a5b3ea43a725a32b0380f4 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 1 Aug 2023 09:14:02 +0200 Subject: [PATCH 05/10] Narrow return type of bsonSerialize implementations --- src/MongoDB/ReadConcern.stub.php | 7 +------ src/MongoDB/ReadConcern_arginfo.h | 21 ++------------------- src/MongoDB/ReadPreference.stub.php | 7 +------ src/MongoDB/ReadPreference_arginfo.h | 21 ++------------------- src/MongoDB/ServerApi.stub.php | 7 +------ src/MongoDB/ServerApi_arginfo.h | 21 ++------------------- src/MongoDB/WriteConcern.stub.php | 7 +------ src/MongoDB/WriteConcern_arginfo.h | 20 ++------------------ 8 files changed, 12 insertions(+), 99 deletions(-) diff --git a/src/MongoDB/ReadConcern.stub.php b/src/MongoDB/ReadConcern.stub.php index 91c4d4de8..00535463c 100644 --- a/src/MongoDB/ReadConcern.stub.php +++ b/src/MongoDB/ReadConcern.stub.php @@ -47,12 +47,7 @@ final public function isDefault(): bool {} final public static function __set_state(array $properties): ReadConcern {} -#if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|\stdClass {} -#else - /** @return array|\stdClass */ - final public function bsonSerialize() {} -#endif + final public function bsonSerialize(): \stdClass {} final public function serialize(): string {} diff --git a/src/MongoDB/ReadConcern_arginfo.h b/src/MongoDB/ReadConcern_arginfo.h index 09145f2d6..8814d6bba 100644 --- a/src/MongoDB/ReadConcern_arginfo.h +++ b/src/MongoDB/ReadConcern_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b8616bced8b4b9fcc10eb931a081c4476ff5d134 */ + * Stub hash: da6e980fbfe2102334158e3018b827e0bb0a9ec3 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, level, IS_STRING, 1, "null") @@ -15,15 +15,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern_ ZEND_ARG_TYPE_INFO(0, properties, IS_ARRAY, 0) ZEND_END_ARG_INFO() -#if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) -ZEND_END_ARG_INFO() -#endif - -#if !(PHP_VERSION_ID >= 80000) -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, 0, 0, stdClass, 0) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ReadConcern_serialize, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -52,12 +45,7 @@ static ZEND_METHOD(MongoDB_Driver_ReadConcern, __construct); static ZEND_METHOD(MongoDB_Driver_ReadConcern, getLevel); static ZEND_METHOD(MongoDB_Driver_ReadConcern, isDefault); static ZEND_METHOD(MongoDB_Driver_ReadConcern, __set_state); -#if PHP_VERSION_ID >= 80000 -static ZEND_METHOD(MongoDB_Driver_ReadConcern, bsonSerialize); -#endif -#if !(PHP_VERSION_ID >= 80000) static ZEND_METHOD(MongoDB_Driver_ReadConcern, bsonSerialize); -#endif static ZEND_METHOD(MongoDB_Driver_ReadConcern, serialize); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ReadConcern, unserialize); @@ -74,12 +62,7 @@ static const zend_function_entry class_MongoDB_Driver_ReadConcern_methods[] = { ZEND_ME(MongoDB_Driver_ReadConcern, getLevel, arginfo_class_MongoDB_Driver_ReadConcern_getLevel, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ReadConcern, isDefault, arginfo_class_MongoDB_Driver_ReadConcern_isDefault, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ReadConcern, __set_state, arginfo_class_MongoDB_Driver_ReadConcern___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) -#if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ReadConcern, bsonSerialize, arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif -#if !(PHP_VERSION_ID >= 80000) - ZEND_ME(MongoDB_Driver_ReadConcern, bsonSerialize, arginfo_class_MongoDB_Driver_ReadConcern_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif ZEND_ME(MongoDB_Driver_ReadConcern, serialize, arginfo_class_MongoDB_Driver_ReadConcern_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ReadConcern, unserialize, arginfo_class_MongoDB_Driver_ReadConcern_unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/MongoDB/ReadPreference.stub.php b/src/MongoDB/ReadPreference.stub.php index 6cabfd5fe..c07e7e1cc 100644 --- a/src/MongoDB/ReadPreference.stub.php +++ b/src/MongoDB/ReadPreference.stub.php @@ -100,12 +100,7 @@ final public function getTagSets(): array {} final public static function __set_state(array $properties): ReadPreference {} -#if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|\stdClass {} -#else - /** @return array|\stdClass */ - final public function bsonSerialize() {} -#endif + final public function bsonSerialize(): \stdClass {} final public function serialize(): string {} diff --git a/src/MongoDB/ReadPreference_arginfo.h b/src/MongoDB/ReadPreference_arginfo.h index 05791b27c..cd04119c2 100644 --- a/src/MongoDB/ReadPreference_arginfo.h +++ b/src/MongoDB/ReadPreference_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9868e4e3be9c2df920f996b7b881c67e2d46e6ea */ + * Stub hash: 1a1a31ef5910ddfbe66ba6c350df216ffe25b5dd */ #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreference___construct, 0, 0, 1) @@ -35,15 +35,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreferen ZEND_ARG_TYPE_INFO(0, properties, IS_ARRAY, 0) ZEND_END_ARG_INFO() -#if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) -ZEND_END_ARG_INFO() -#endif - -#if !(PHP_VERSION_ID >= 80000) -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, 0, 0, stdClass, 0) ZEND_END_ARG_INFO() -#endif #define arginfo_class_MongoDB_Driver_ReadPreference_serialize arginfo_class_MongoDB_Driver_ReadPreference_getModeString @@ -78,12 +71,7 @@ static ZEND_METHOD(MongoDB_Driver_ReadPreference, getMode); static ZEND_METHOD(MongoDB_Driver_ReadPreference, getModeString); static ZEND_METHOD(MongoDB_Driver_ReadPreference, getTagSets); static ZEND_METHOD(MongoDB_Driver_ReadPreference, __set_state); -#if PHP_VERSION_ID >= 80000 -static ZEND_METHOD(MongoDB_Driver_ReadPreference, bsonSerialize); -#endif -#if !(PHP_VERSION_ID >= 80000) static ZEND_METHOD(MongoDB_Driver_ReadPreference, bsonSerialize); -#endif static ZEND_METHOD(MongoDB_Driver_ReadPreference, serialize); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ReadPreference, unserialize); @@ -108,12 +96,7 @@ static const zend_function_entry class_MongoDB_Driver_ReadPreference_methods[] = ZEND_ME(MongoDB_Driver_ReadPreference, getModeString, arginfo_class_MongoDB_Driver_ReadPreference_getModeString, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ReadPreference, getTagSets, arginfo_class_MongoDB_Driver_ReadPreference_getTagSets, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ReadPreference, __set_state, arginfo_class_MongoDB_Driver_ReadPreference___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) -#if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ReadPreference, bsonSerialize, arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif -#if !(PHP_VERSION_ID >= 80000) - ZEND_ME(MongoDB_Driver_ReadPreference, bsonSerialize, arginfo_class_MongoDB_Driver_ReadPreference_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif ZEND_ME(MongoDB_Driver_ReadPreference, serialize, arginfo_class_MongoDB_Driver_ReadPreference_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ReadPreference, unserialize, arginfo_class_MongoDB_Driver_ReadPreference_unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/MongoDB/ServerApi.stub.php b/src/MongoDB/ServerApi.stub.php index 6a99e6976..fd907d5ca 100644 --- a/src/MongoDB/ServerApi.stub.php +++ b/src/MongoDB/ServerApi.stub.php @@ -16,12 +16,7 @@ final public function __construct(string $version, ?bool $strict = null, ?bool $ final public static function __set_state(array $properties): ServerApi {} -#if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|\stdClass {} -#else - /** @return array|\stdClass */ - final public function bsonSerialize() {} -#endif + final public function bsonSerialize(): \stdClass {} final public function serialize(): string {} diff --git a/src/MongoDB/ServerApi_arginfo.h b/src/MongoDB/ServerApi_arginfo.h index 4967f9647..f3da02d42 100644 --- a/src/MongoDB/ServerApi_arginfo.h +++ b/src/MongoDB/ServerApi_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 50ac33dea8943336b9c562a90e189036c90f1479 */ + * Stub hash: 7678bfacdb398a3528feb4226a5ca1b8a21b9516 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, version, IS_STRING, 0) @@ -11,15 +11,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi___ ZEND_ARG_TYPE_INFO(0, properties, IS_ARRAY, 0) ZEND_END_ARG_INFO() -#if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) -ZEND_END_ARG_INFO() -#endif - -#if !(PHP_VERSION_ID >= 80000) -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, 0, 0, stdClass, 0) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_ServerApi_serialize, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -46,12 +39,7 @@ ZEND_END_ARG_INFO() static ZEND_METHOD(MongoDB_Driver_ServerApi, __construct); static ZEND_METHOD(MongoDB_Driver_ServerApi, __set_state); -#if PHP_VERSION_ID >= 80000 -static ZEND_METHOD(MongoDB_Driver_ServerApi, bsonSerialize); -#endif -#if !(PHP_VERSION_ID >= 80000) static ZEND_METHOD(MongoDB_Driver_ServerApi, bsonSerialize); -#endif static ZEND_METHOD(MongoDB_Driver_ServerApi, serialize); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_ServerApi, unserialize); @@ -66,12 +54,7 @@ static ZEND_METHOD(MongoDB_Driver_ServerApi, __serialize); static const zend_function_entry class_MongoDB_Driver_ServerApi_methods[] = { ZEND_ME(MongoDB_Driver_ServerApi, __construct, arginfo_class_MongoDB_Driver_ServerApi___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_ServerApi, __set_state, arginfo_class_MongoDB_Driver_ServerApi___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) -#if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ServerApi, bsonSerialize, arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif -#if !(PHP_VERSION_ID >= 80000) - ZEND_ME(MongoDB_Driver_ServerApi, bsonSerialize, arginfo_class_MongoDB_Driver_ServerApi_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif ZEND_ME(MongoDB_Driver_ServerApi, serialize, arginfo_class_MongoDB_Driver_ServerApi_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_ServerApi, unserialize, arginfo_class_MongoDB_Driver_ServerApi_unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) diff --git a/src/MongoDB/WriteConcern.stub.php b/src/MongoDB/WriteConcern.stub.php index afd212de4..d02c3aa80 100644 --- a/src/MongoDB/WriteConcern.stub.php +++ b/src/MongoDB/WriteConcern.stub.php @@ -37,12 +37,7 @@ final public function isDefault(): bool {} final public static function __set_state(array $properties): WriteConcern {} -#if PHP_VERSION_ID >= 80000 - final public function bsonSerialize(): array|\stdClass {} -#else - /** @return array|\stdClass */ - final public function bsonSerialize() {} -#endif + final public function bsonSerialize(): \stdClass {} final public function serialize(): string {} diff --git a/src/MongoDB/WriteConcern_arginfo.h b/src/MongoDB/WriteConcern_arginfo.h index 68a99a6df..3a407e1a5 100644 --- a/src/MongoDB/WriteConcern_arginfo.h +++ b/src/MongoDB/WriteConcern_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: bc052f233ec885eb7209a77d32fd9290a2a101e3 */ + * Stub hash: 690913f0505161b1b2ca35495c4cdb06c263aacb */ #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern___construct, 0, 0, 1) @@ -40,14 +40,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern ZEND_ARG_TYPE_INFO(0, properties, IS_ARRAY, 0) ZEND_END_ARG_INFO() -#if PHP_VERSION_ID >= 80000 -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, 0, 0, stdClass, MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, 0, 0, stdClass, 0) ZEND_END_ARG_INFO() -#endif - -#if !(PHP_VERSION_ID >= 80000) -#define arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize arginfo_class_MongoDB_Driver_WriteConcern_getW -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_MongoDB_Driver_WriteConcern_serialize, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -88,12 +82,7 @@ static ZEND_METHOD(MongoDB_Driver_WriteConcern, getW); static ZEND_METHOD(MongoDB_Driver_WriteConcern, getWtimeout); static ZEND_METHOD(MongoDB_Driver_WriteConcern, isDefault); static ZEND_METHOD(MongoDB_Driver_WriteConcern, __set_state); -#if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_WriteConcern, bsonSerialize); -#endif -#if !(PHP_VERSION_ID >= 80000) -static ZEND_METHOD(MongoDB_Driver_WriteConcern, bsonSerialize); -#endif static ZEND_METHOD(MongoDB_Driver_WriteConcern, serialize); #if PHP_VERSION_ID >= 80000 static ZEND_METHOD(MongoDB_Driver_WriteConcern, unserialize); @@ -122,12 +111,7 @@ static const zend_function_entry class_MongoDB_Driver_WriteConcern_methods[] = { ZEND_ME(MongoDB_Driver_WriteConcern, getWtimeout, arginfo_class_MongoDB_Driver_WriteConcern_getWtimeout, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_WriteConcern, isDefault, arginfo_class_MongoDB_Driver_WriteConcern_isDefault, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(MongoDB_Driver_WriteConcern, __set_state, arginfo_class_MongoDB_Driver_WriteConcern___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) -#if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_WriteConcern, bsonSerialize, arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif -#if !(PHP_VERSION_ID >= 80000) - ZEND_ME(MongoDB_Driver_WriteConcern, bsonSerialize, arginfo_class_MongoDB_Driver_WriteConcern_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) -#endif ZEND_ME(MongoDB_Driver_WriteConcern, serialize, arginfo_class_MongoDB_Driver_WriteConcern_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) #if PHP_VERSION_ID >= 80000 ZEND_ME(MongoDB_Driver_WriteConcern, unserialize, arginfo_class_MongoDB_Driver_WriteConcern_unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) From fc4f83cc405e3ab164c6848147315f1ce9064195 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 1 Aug 2023 09:18:54 +0200 Subject: [PATCH 06/10] Split BSON structure checks for legibility --- src/phongo_bson_encode.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index 0b20801f1..d88f40099 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -104,14 +104,16 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f } if (Z_TYPE_P(object) == IS_OBJECT && instanceof_function(Z_OBJCE_P(object), php_phongo_type_ce)) { - if (Z_TYPE_P(object) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce) || instanceof_function(Z_OBJCE_P(object), php_phongo_packedarray_ce))) { - if (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce)) { - php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(object); - bson_append_document(bson, key, key_len, intern->bson); - } else { - php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(object); - bson_append_array(bson, key, key_len, intern->bson); - } + if (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce)) { + php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(object); + bson_append_document(bson, key, key_len, intern->bson); + + return; + } + + if (instanceof_function(Z_OBJCE_P(object), php_phongo_packedarray_ce)) { + php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(object); + bson_append_array(bson, key, key_len, intern->bson); return; } From 8622a865e6a66d6e8cae3f12399a76b35b12cdcc Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 1 Aug 2023 09:23:12 +0200 Subject: [PATCH 07/10] Added todo to remove obsolete type check --- src/phongo_bson_encode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index d88f40099..0d997a580 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -131,6 +131,7 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f return; } + // TODO PHP_VERSION_ID < 80000: obsolete once the tentative return type of bsonSerialize() is enforced if ( Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, From 07ebeeb1fd80c9c291dfc5fe382716cbf7698b1d Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 1 Aug 2023 09:31:14 +0200 Subject: [PATCH 08/10] Simplify handling of arrays when encoding BSON --- src/phongo_bson_encode.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index 0d997a580..271e38b48 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -58,6 +58,10 @@ static int php_phongo_is_array_or_document(zval* val) int count; if (Z_TYPE_P(val) != IS_ARRAY) { + if (Z_TYPE_P(val) == IS_OBJECT && instanceof_function(Z_OBJCE_P(val), php_phongo_packedarray_ce)) { + return IS_ARRAY; + } + return IS_OBJECT; } @@ -121,7 +125,6 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f if (instanceof_function(Z_OBJCE_P(object), php_phongo_serializable_ce)) { zval obj_data; bson_t child; - bool is_array; zend_call_method_with_0_params(PHONGO_COMPAT_OBJ_P(object), NULL, NULL, BSON_SERIALIZE_FUNC_NAME, &obj_data); @@ -146,11 +149,9 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f return; } - is_array = php_phongo_is_array_or_document(&obj_data) == IS_ARRAY || (Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)); - /* Persistable objects must always be serialized as BSON documents; * otherwise, infer based on bsonSerialize()'s return value. */ - if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce) || !is_array) { + if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce) || php_phongo_is_array_or_document(&obj_data) != IS_ARRAY) { bson_append_document_begin(bson, key, key_len, &child); if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce)) { bson_append_binary(&child, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(object)->name->val, Z_OBJCE_P(object)->name->len); From 1516d3b97108a03094141bc64b243a75b3671585 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 1 Aug 2023 09:31:34 +0200 Subject: [PATCH 09/10] Prevent Persistable implementations to return PackedArray instances --- src/BSON/Persistable.stub.php | 7 +++++++ src/BSON/Persistable_arginfo.h | 22 +++++++++++++++++++++- src/phongo_bson_encode.c | 27 ++++++++++++++++++++++++++- tests/bson/bson-fromPHP-001.phpt | 5 +++-- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/BSON/Persistable.stub.php b/src/BSON/Persistable.stub.php index bed2c1244..be81e032b 100644 --- a/src/BSON/Persistable.stub.php +++ b/src/BSON/Persistable.stub.php @@ -9,4 +9,11 @@ interface Persistable extends Serializable, Unserializable { +#if PHP_VERSION_ID >= 80000 + /** @tentative-return-type */ + public function bsonSerialize(): array|\stdClass|Document; +#else + /** @return array|\stdClass|Document */ + public function bsonSerialize(); +#endif } diff --git a/src/BSON/Persistable_arginfo.h b/src/BSON/Persistable_arginfo.h index eb35a440f..0c7048f2b 100644 --- a/src/BSON/Persistable_arginfo.h +++ b/src/BSON/Persistable_arginfo.h @@ -1,10 +1,30 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: babd07f95f47c3b66228ef23b66ab0446cd5c308 */ + * Stub hash: e61c06a90093af5468c6c29f6cbf16c5db8d54d1 */ +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_MongoDB_BSON_Persistable_bsonSerialize, 0, 0, stdClass|MongoDB\\BSON\\Document, MAY_BE_ARRAY) +ZEND_END_ARG_INFO() +#endif +#if !(PHP_VERSION_ID >= 80000) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MongoDB_BSON_Persistable_bsonSerialize, 0, 0, 0) +ZEND_END_ARG_INFO() +#endif + + +#if PHP_VERSION_ID >= 80000 +#endif +#if !(PHP_VERSION_ID >= 80000) +#endif static const zend_function_entry class_MongoDB_BSON_Persistable_methods[] = { +#if PHP_VERSION_ID >= 80000 + ZEND_ABSTRACT_ME_WITH_FLAGS(MongoDB_BSON_Persistable, bsonSerialize, arginfo_class_MongoDB_BSON_Persistable_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT) +#endif +#if !(PHP_VERSION_ID >= 80000) + ZEND_ABSTRACT_ME_WITH_FLAGS(MongoDB_BSON_Persistable, bsonSerialize, arginfo_class_MongoDB_BSON_Persistable_bsonSerialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT) +#endif ZEND_FE_END }; diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index 271e38b48..681001ffb 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -135,7 +135,20 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f } // TODO PHP_VERSION_ID < 80000: obsolete once the tentative return type of bsonSerialize() is enforced - if ( + if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce)) { + if ( + Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce)))) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, + "Expected %s::%s() to return an array, stdClass, or %s, %s given", + ZSTR_VAL(Z_OBJCE_P(object)->name), + BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); + zval_ptr_dtor(&obj_data); + + return; + } + } else if ( Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", @@ -462,6 +475,18 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* } if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { + if (Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)) { + phongo_throw_exception( + PHONGO_ERROR_UNEXPECTED_VALUE, + "Expected %s::%s() to return an array, stdClass, or %s, %s given", + ZSTR_VAL(Z_OBJCE_P(data)->name), + BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); + + goto cleanup; + } + bson_append_binary(bson, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(data)->name->val, Z_OBJCE_P(data)->name->len); /* Ensure that we ignore an existing key with the same name * if one exists in the bsonSerialize() return value. */ diff --git a/tests/bson/bson-fromPHP-001.phpt b/tests/bson/bson-fromPHP-001.phpt index b104fa272..55016f392 100644 --- a/tests/bson/bson-fromPHP-001.phpt +++ b/tests/bson/bson-fromPHP-001.phpt @@ -34,6 +34,7 @@ $tests = array( array('foo' => 'bar'), (object) array(1, 2, 3), (object) array('foo' => 'bar'), + # The PackedArray check will fail for instances of Persistable MongoDB\BSON\PackedArray::fromPHP([1, 2, 3]), MongoDB\BSON\Document::fromPHP(['foo' => 'bar']), ); @@ -74,7 +75,7 @@ Testing top-level objects { "foo" : "bar" } { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } { "0" : 1, "1" : 2, "2" : 3 } -{ "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } +Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given { "foo" : "bar" } { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } @@ -88,7 +89,7 @@ Testing nested objects { "nested" : { "foo" : "bar" } } { "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } } { "nested" : [ 1, 2, 3 ] } -{ "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "0" : 1, "1" : 2, "2" : 3 } } +Expected MyPersistableDocument::bsonSerialize() to return an array, stdClass, or MongoDB\BSON\Document, MongoDB\BSON\PackedArray given { "nested" : { "foo" : "bar" } } { "nested" : { "__pclass" : { "$binary" : "TXlQZXJzaXN0YWJsZURvY3VtZW50", "$type" : "80" }, "foo" : "bar" } } ===DONE=== From 3c01dd2650af16fbf239fc3ee37ad41aefe880f0 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Wed, 2 Aug 2023 08:50:39 +0200 Subject: [PATCH 10/10] Extract utility function to check return types of bsonSerialize() --- src/phongo_bson_encode.c | 99 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/phongo_bson_encode.c b/src/phongo_bson_encode.c index 681001ffb..7780e877d 100644 --- a/src/phongo_bson_encode.c +++ b/src/phongo_bson_encode.c @@ -90,6 +90,49 @@ static int php_phongo_is_array_or_document(zval* val) return IS_ARRAY; } +/* Checks the return type of a bsonSerialize() method. Returns true on + * success; otherwise, throws an exception and returns false. + * + * TODO: obsolete once PHP 8.0+ is required and tentative return type is enforced. + */ +static inline bool phongo_check_bson_serialize_return_type(zval* retval, zend_class_entry* ce) +{ + if (instanceof_function(ce, php_phongo_persistable_ce)) { + // Instances of Persistable must return an array, stdClass, or MongoDB\BSON\Document + if ( + Z_TYPE_P(retval) != IS_ARRAY && !(Z_TYPE_P(retval) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(retval), zend_standard_class_def) || instanceof_function(Z_OBJCE_P(retval), php_phongo_document_ce)))) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, + "Expected %s::%s() to return an array, stdClass, or %s, %s given", + ZSTR_VAL(ce->name), + BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + PHONGO_ZVAL_CLASS_OR_TYPE_NAME(*retval)); + return false; + } + + return true; + } + + if (instanceof_function(ce, php_phongo_serializable_ce)) { + // Instances of Serializable must return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray + if ( + Z_TYPE_P(retval) != IS_ARRAY && !(Z_TYPE_P(retval) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(retval), zend_standard_class_def) || instanceof_function(Z_OBJCE_P(retval), php_phongo_document_ce) || instanceof_function(Z_OBJCE_P(retval), php_phongo_packedarray_ce)))) { + phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, + "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", + ZSTR_VAL(ce->name), + BSON_SERIALIZE_FUNC_NAME, + ZSTR_VAL(php_phongo_document_ce->name), + ZSTR_VAL(php_phongo_packedarray_ce->name), + PHONGO_ZVAL_CLASS_OR_TYPE_NAME(*retval)); + return false; + } + + return true; + } + + return false; +} + /* Appends the array or object argument to the BSON document. * * For instances of MongoDB\BSON\Document, raw BSON data is appended as document. @@ -134,31 +177,9 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* f return; } - // TODO PHP_VERSION_ID < 80000: obsolete once the tentative return type of bsonSerialize() is enforced - if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce)) { - if ( - Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce)))) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array, stdClass, or %s, %s given", - ZSTR_VAL(Z_OBJCE_P(object)->name), - BSON_SERIALIZE_FUNC_NAME, - ZSTR_VAL(php_phongo_document_ce->name), - PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); - zval_ptr_dtor(&obj_data); - - return; - } - } else if ( - Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def) || instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)))) { - phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", - ZSTR_VAL(Z_OBJCE_P(object)->name), - BSON_SERIALIZE_FUNC_NAME, - ZSTR_VAL(php_phongo_document_ce->name), - ZSTR_VAL(php_phongo_packedarray_ce->name), - PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); + if (!phongo_check_bson_serialize_return_type(&obj_data, Z_OBJCE_P(object))) { + // Exception already thrown zval_ptr_dtor(&obj_data); - return; } @@ -474,19 +495,12 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* return; } - if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { - if (Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce)) { - phongo_throw_exception( - PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array, stdClass, or %s, %s given", - ZSTR_VAL(Z_OBJCE_P(data)->name), - BSON_SERIALIZE_FUNC_NAME, - ZSTR_VAL(php_phongo_document_ce->name), - PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); - - goto cleanup; - } + if (!phongo_check_bson_serialize_return_type(&obj_data, Z_OBJCE_P(data))) { + // Exception already thrown + goto cleanup; + } + if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { bson_append_binary(bson, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(data)->name->val, Z_OBJCE_P(data)->name->len); /* Ensure that we ignore an existing key with the same name * if one exists in the bsonSerialize() return value. */ @@ -500,19 +514,6 @@ static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* goto done; } - if (Z_TYPE(obj_data) != IS_ARRAY && !(Z_TYPE(obj_data) == IS_OBJECT && instanceof_function(Z_OBJCE(obj_data), zend_standard_class_def))) { - phongo_throw_exception( - PHONGO_ERROR_UNEXPECTED_VALUE, - "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", - ZSTR_VAL(Z_OBJCE_P(data)->name), - BSON_SERIALIZE_FUNC_NAME, - ZSTR_VAL(php_phongo_document_ce->name), - ZSTR_VAL(php_phongo_packedarray_ce->name), - PHONGO_ZVAL_CLASS_OR_TYPE_NAME(obj_data)); - - goto cleanup; - } - ht_data = HASH_OF(&obj_data); break;