Skip to content

PHPC-359 and PHPC-801: RC, RP, and WC BSON serialization #424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions php_phongo.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,49 @@ void php_phongo_read_concern_to_zval(zval *retval, const mongoc_read_concern_t *
}
} /* }}} */

/* Prepare tagSets for BSON encoding by converting each array in the set to an
* object. This ensures that empty arrays will serialize as empty documents.
*
* php_phongo_read_preference_tags_are_valid() handles actual validation of the
* tag set structure. */
void php_phongo_read_preference_prep_tagsets(zval *tagSets TSRMLS_DC) /* {{{ */
{
HashTable *ht_data;

if (Z_TYPE_P(tagSets) != IS_ARRAY) {
return;
}

ht_data = HASH_OF(tagSets);

#if PHP_VERSION_ID >= 70000
{
zval *tagSet;

ZEND_HASH_FOREACH_VAL(ht_data, tagSet) {
if (Z_TYPE_P(tagSet) == IS_ARRAY) {
convert_to_object(tagSet);
}
} ZEND_HASH_FOREACH_END();
}
#else
{
HashPosition pos;
zval **tagSet;

for (zend_hash_internal_pointer_reset_ex(ht_data, &pos);
zend_hash_get_current_data_ex(ht_data, (void **) &tagSet, &pos) == SUCCESS;
zend_hash_move_forward_ex(ht_data, &pos)) {
if (Z_TYPE_PP(tagSet) == IS_ARRAY) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like whitespace issues here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct. The for components are all indented five spaces and the if (and lines below) are indented with a tab, since they're within the for block.

convert_to_object(*tagSet);
}
}
}
#endif

return;
} /* }}} */

/* Checks if tags is valid to set on a mongoc_read_prefs_t. It may be null or an
* array of one or more documents. */
bool php_phongo_read_preference_tags_are_valid(const bson_t *tags) /* {{{ */
Expand Down Expand Up @@ -864,10 +907,10 @@ void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t
}

if (!bson_empty0(tags)) {
/* Use PHONGO_TYPEMAP_NATIVE_ARRAY for the root type since tags is an
* array; however, inner documents and arrays can use the default. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be explicit about the document_type? i.e., by setting it to something like:

state.map.document_type = PHONGO_TYPEMAP_DEFAULT;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHONGO_BSON_STATE_INITIALIZER is already the default, which we use for the state intialization. This should suffice.

php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
/* Use native arrays for debugging output */
state.map.root_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
state.map.document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;

phongo_bson_to_zval_ex(bson_get_data(tags), tags->len, &state);
#if PHP_VERSION_ID >= 70000
Expand Down
1 change: 1 addition & 0 deletions php_phongo.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const mongoc_write_concern_t* phongo_write_concern_from_zval (zval *zwrite_conc

php_phongo_server_description_type_t php_phongo_server_description_type(mongoc_server_description_t *sd);

void php_phongo_read_preference_prep_tagsets(zval *tagSets TSRMLS_DC);
bool php_phongo_read_preference_tags_are_valid(const bson_t *tags);

void php_phongo_server_to_zval(zval *retval, mongoc_server_description_t *sd);
Expand Down
65 changes: 65 additions & 0 deletions src/MongoDB/Manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,67 @@ static bool php_phongo_manager_merge_context_options(zval *zdriverOptions TSRMLS
return true;
}

/* Prepare tagSets for BSON encoding by converting each array in the set to an
* object. This ensures that empty arrays will serialize as empty documents.
*
* php_phongo_read_preference_tags_are_valid() handles actual validation of the
* tag set structure. */
static void php_phongo_manager_prep_tagsets(zval *options TSRMLS_DC)
{
HashTable *ht_data;

if (Z_TYPE_P(options) != IS_ARRAY) {
return;
}

ht_data = HASH_OF(options);

#if PHP_VERSION_ID >= 70000
{
zend_string *string_key = NULL;
zend_ulong num_key = 0;
zval *tagSets;

ZEND_HASH_FOREACH_KEY_VAL(ht_data, num_key, string_key, tagSets) {
if (!string_key) {
continue;
}

/* php_phongo_make_uri() and php_phongo_apply_rp_options_to_uri()
* are both case-insensitive, so we need to be as well. */
if (!strcasecmp(ZSTR_VAL(string_key), "readpreferencetags")) {
php_phongo_read_preference_prep_tagsets(tagSets TSRMLS_CC);
}
} ZEND_HASH_FOREACH_END();
}
#else
{
HashPosition pos;
zval **tagSets;

for (zend_hash_internal_pointer_reset_ex(ht_data, &pos);
zend_hash_get_current_data_ex(ht_data, (void **) &tagSets, &pos) == SUCCESS;
zend_hash_move_forward_ex(ht_data, &pos)) {
char *string_key = NULL;
uint string_key_len = 0;
ulong num_key = 0;

if (HASH_KEY_IS_STRING != zend_hash_get_current_key_ex(ht_data, &string_key, &string_key_len, &num_key, 0, &pos)) {
continue;
}

/* php_phongo_make_uri() and php_phongo_apply_rp_options_to_uri()
* are both case-insensitive, so we need to be as well. */
if (!strcasecmp(string_key, "readpreferencetags")) {
php_phongo_read_preference_prep_tagsets(*tagSets TSRMLS_CC);
}
}
}
#endif

return;
} /* }}} */

/* {{{ proto void Manager::__construct([string $uri = "mongodb://127.0.0.1/"[, array $options = array()[, array $driverOptions = array()]]])
Constructs a new Manager */
PHP_METHOD(Manager, __construct)
Expand All @@ -124,6 +185,10 @@ PHP_METHOD(Manager, __construct)
}
zend_restore_error_handling(&error_handling TSRMLS_CC);

if (options) {
php_phongo_manager_prep_tagsets(options TSRMLS_CC);
}

if (driverOptions && !php_phongo_manager_merge_context_options(driverOptions TSRMLS_CC)) {
/* Exception should already have been thrown */
return;
Expand Down
1 change: 1 addition & 0 deletions src/MongoDB/ReadConcern.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ PHP_METHOD(ReadConcern, bsonSerialize)
}

php_phongo_read_concern_to_zval(return_value, read_concern);
convert_to_object(return_value);
}
/* }}} */

Expand Down
2 changes: 2 additions & 0 deletions src/MongoDB/ReadPreference.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ PHP_METHOD(ReadPreference, __construct)
if (tagSets) {
bson_t *tags = bson_new();

php_phongo_read_preference_prep_tagsets(tagSets TSRMLS_CC);
phongo_zval_to_bson(tagSets, PHONGO_BSON_NONE, (bson_t *)tags, NULL TSRMLS_CC);

if (!php_phongo_read_preference_tags_are_valid(tags)) {
Expand Down Expand Up @@ -211,6 +212,7 @@ PHP_METHOD(ReadPreference, bsonSerialize)
}

php_phongo_read_preference_to_zval(return_value, read_preference);
convert_to_object(return_value);
}
/* }}} */

Expand Down
1 change: 1 addition & 0 deletions src/MongoDB/WriteConcern.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ PHP_METHOD(WriteConcern, bsonSerialize)
}

php_phongo_write_concern_to_zval(return_value, write_concern);
convert_to_object(return_value);
}
/* }}} */

Expand Down
8 changes: 4 additions & 4 deletions tests/manager/manager-ctor-read_preference-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(2) {
[0]=>
array(1) {
object(stdClass)#%d (%d) {
["tag"]=>
string(3) "one"
}
[1]=>
array(0) {
object(stdClass)#%d (%d) {
}
}
}
Expand All @@ -58,12 +58,12 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(2) {
[0]=>
array(1) {
object(stdClass)#%d (%d) {
["tag"]=>
string(3) "one"
}
[1]=>
array(0) {
object(stdClass)#%d (%d) {
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/manager/manager-ctor-read_preference-003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(1) {
[0]=>
array(1) {
object(stdClass)#%d (%d) {
["tag"]=>
string(3) "one"
}
Expand Down
10 changes: 5 additions & 5 deletions tests/manager/manager-getreadpreference-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(2) {
[0]=>
array(2) {
object(stdClass)#%d (%d) {
["dc"]=>
string(2) "ny"
["use"]=>
string(7) "reports"
}
[1]=>
array(0) {
object(stdClass)#%d (%d) {
}
}
}
Expand All @@ -69,14 +69,14 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(2) {
[0]=>
array(2) {
object(stdClass)#%d (%d) {
["dc"]=>
string(2) "ny"
["use"]=>
string(7) "reports"
}
[1]=>
array(0) {
object(stdClass)#%d (%d) {
}
}
}
Expand All @@ -86,7 +86,7 @@ object(MongoDB\Driver\ReadPreference)#%d (%d) {
["tags"]=>
array(1) {
[0]=>
array(1) {
object(stdClass)#%d (%d) {
["dc"]=>
string(2) "ca"
}
Expand Down
27 changes: 27 additions & 0 deletions tests/readConcern/readconcern-bsonserialize-001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
MongoDB\Driver\ReadConcern::bsonSerialize()
--FILE--
<?php

require_once __DIR__ . '/../utils/tools.php';

$tests = [
new MongoDB\Driver\ReadConcern(),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LINEARIZABLE),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LOCAL),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY),
];

foreach ($tests as $test) {
echo toJSON(fromPHP($test)), "\n";
}

?>
===DONE===
<?php exit(0); ?>
--EXPECT--
{ }
{ "level" : "linearizable" }
{ "level" : "local" }
{ "level" : "majority" }
===DONE===
37 changes: 37 additions & 0 deletions tests/readConcern/readconcern-bsonserialize-002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
MongoDB\Driver\ReadConcern::bsonSerialize() returns an object
--FILE--
<?php

require_once __DIR__ . '/../utils/tools.php';

$tests = [
new MongoDB\Driver\ReadConcern(),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LINEARIZABLE),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LOCAL),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY),
];

foreach ($tests as $test) {
var_dump($test->bsonSerialize());
}

?>
===DONE===
<?php exit(0); ?>
--EXPECTF--
object(stdClass)#%d (%d) {
}
object(stdClass)#%d (%d) {
["level"]=>
string(12) "linearizable"
}
object(stdClass)#%d (%d) {
["level"]=>
string(5) "local"
}
object(stdClass)#%d (%d) {
["level"]=>
string(8) "majority"
}
===DONE===
2 changes: 2 additions & 0 deletions tests/readConcern/readconcern-constants.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ MongoDB\Driver\ReadConcern constants
<?php
require_once __DIR__ . "/../utils/basic.inc";

var_dump(MongoDB\Driver\ReadConcern::LINEARIZABLE);
var_dump(MongoDB\Driver\ReadConcern::LOCAL);
var_dump(MongoDB\Driver\ReadConcern::MAJORITY);

?>
===DONE===
<?php exit(0); ?>
--EXPECTF--
string(12) "linearizable"
string(5) "local"
string(8) "majority"
===DONE===
37 changes: 37 additions & 0 deletions tests/readConcern/readconcern-debug-001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
MongoDB\Driver\ReadConcern debug output
--FILE--
<?php

require_once __DIR__ . '/../utils/tools.php';

$tests = [
new MongoDB\Driver\ReadConcern(),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LINEARIZABLE),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::LOCAL),
new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY),
];

foreach ($tests as $test) {
var_dump($test);
}

?>
===DONE===
<?php exit(0); ?>
--EXPECTF--
object(MongoDB\Driver\ReadConcern)#%d (%d) {
}
object(MongoDB\Driver\ReadConcern)#%d (%d) {
["level"]=>
string(12) "linearizable"
}
object(MongoDB\Driver\ReadConcern)#%d (%d) {
["level"]=>
string(5) "local"
}
object(MongoDB\Driver\ReadConcern)#%d (%d) {
["level"]=>
string(8) "majority"
}
===DONE===
Loading