Skip to content

Commit 9cbb01a

Browse files
committed
PHPC-1206: Add __set_state() support for ReadPreference
1 parent b03d171 commit 9cbb01a

File tree

5 files changed

+343
-2
lines changed

5 files changed

+343
-2
lines changed

src/MongoDB/ReadPreference.c

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,162 @@
2828

2929
zend_class_entry* php_phongo_readpreference_ce;
3030

31+
/* Initialize the object from a HashTable and return whether it was successful.
32+
* An exception will be thrown on error. */
33+
static bool php_phongo_readpreference_init_from_hash(php_phongo_readpreference_t* intern, HashTable* props TSRMLS_DC) /* {{{ */
34+
{
35+
#if PHP_VERSION_ID >= 70000
36+
zval *mode, *tagSets, *maxStalenessSeconds;
37+
38+
if ((mode = zend_hash_str_find(props, "mode", sizeof("mode") - 1)) && Z_TYPE_P(mode) == IS_STRING) {
39+
if (strcasecmp(Z_STRVAL_P(mode), "primary") == 0) {
40+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
41+
} else if (strcasecmp(Z_STRVAL_P(mode), "primaryPreferred") == 0) {
42+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_PRIMARY_PREFERRED);
43+
} else if (strcasecmp(Z_STRVAL_P(mode), "secondary") == 0) {
44+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_SECONDARY);
45+
} else if (strcasecmp(Z_STRVAL_P(mode), "secondaryPreferred") == 0) {
46+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_SECONDARY_PREFERRED);
47+
} else if (strcasecmp(Z_STRVAL_P(mode), "nearest") == 0) {
48+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_NEAREST);
49+
} else {
50+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires specific values for \"mode\" string field", ZSTR_VAL(php_phongo_readpreference_ce->name));
51+
return false;
52+
}
53+
} else {
54+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"mode\" field to be string", ZSTR_VAL(php_phongo_readpreference_ce->name));
55+
return false;
56+
}
57+
58+
if ((tagSets = zend_hash_str_find(props, "tags", sizeof("tags") - 1))) {
59+
if (Z_TYPE_P(tagSets) == IS_ARRAY) {
60+
bson_t* tags = bson_new();
61+
62+
php_phongo_read_preference_prep_tagsets(tagSets TSRMLS_CC);
63+
php_phongo_zval_to_bson(tagSets, PHONGO_BSON_NONE, (bson_t*) tags, NULL TSRMLS_CC);
64+
65+
if (!php_phongo_read_preference_tags_are_valid(tags)) {
66+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" array field to have zero or more documents", ZSTR_VAL(php_phongo_writeconcern_ce->name));
67+
bson_destroy(tags);
68+
goto failure;
69+
}
70+
71+
if (!bson_empty(tags) && (mongoc_read_prefs_get_mode(intern->read_preference) == MONGOC_READ_PRIMARY)) {
72+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" array field to not be present with \"primary\" mode", ZSTR_VAL(php_phongo_writeconcern_ce->name));
73+
bson_destroy(tags);
74+
goto failure;
75+
}
76+
77+
mongoc_read_prefs_set_tags(intern->read_preference, tags);
78+
bson_destroy(tags);
79+
} else {
80+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" field to be array", ZSTR_VAL(php_phongo_writeconcern_ce->name));
81+
goto failure;
82+
}
83+
}
84+
85+
if ((maxStalenessSeconds = zend_hash_str_find(props, "maxStalenessSeconds", sizeof("maxStalenessSeconds") - 1))) {
86+
if (Z_TYPE_P(maxStalenessSeconds) == IS_LONG) {
87+
if (Z_LVAL_P(maxStalenessSeconds) < MONGOC_SMALLEST_MAX_STALENESS_SECONDS) {
88+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" integer field to be >= %d", ZSTR_VAL(php_phongo_writeconcern_ce->name), MONGOC_SMALLEST_MAX_STALENESS_SECONDS);
89+
goto failure;
90+
}
91+
if (Z_LVAL_P(maxStalenessSeconds) > INT32_MAX) {
92+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" integer field to be <= %" PRId32, ZSTR_VAL(php_phongo_writeconcern_ce->name), INT32_MAX);
93+
goto failure;
94+
}
95+
if (mongoc_read_prefs_get_mode(intern->read_preference) == MONGOC_READ_PRIMARY) {
96+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" array field to not be present with \"primary\" mode", ZSTR_VAL(php_phongo_writeconcern_ce->name));
97+
goto failure;
98+
}
99+
100+
mongoc_read_prefs_set_max_staleness_seconds(intern->read_preference, Z_LVAL_P(maxStalenessSeconds));
101+
} else {
102+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" field to be integer", ZSTR_VAL(php_phongo_writeconcern_ce->name));
103+
goto failure;
104+
}
105+
}
106+
#else
107+
zval **mode, **tagSets, **maxStalenessSeconds;
108+
109+
if (zend_hash_find(props, "mode", sizeof("mode"), (void**) &mode) == SUCCESS && Z_TYPE_PP(mode) == IS_STRING) {
110+
if (strcasecmp(Z_STRVAL_PP(mode), "primary") == 0) {
111+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
112+
} else if (strcasecmp(Z_STRVAL_PP(mode), "primaryPreferred") == 0) {
113+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_PRIMARY_PREFERRED);
114+
} else if (strcasecmp(Z_STRVAL_PP(mode), "secondary") == 0) {
115+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_SECONDARY);
116+
} else if (strcasecmp(Z_STRVAL_PP(mode), "secondaryPreferred") == 0) {
117+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_SECONDARY_PREFERRED);
118+
} else if (strcasecmp(Z_STRVAL_PP(mode), "nearest") == 0) {
119+
intern->read_preference = mongoc_read_prefs_new(MONGOC_READ_NEAREST);
120+
} else {
121+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires specific values for \"mode\" string field", ZSTR_VAL(php_phongo_readpreference_ce->name));
122+
return false;
123+
}
124+
} else {
125+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"mode\" field to be string", ZSTR_VAL(php_phongo_readpreference_ce->name));
126+
return false;
127+
}
128+
129+
if (zend_hash_find(props, "tags", sizeof("tags"), (void**) &tagSets) == SUCCESS) {
130+
if (Z_TYPE_PP(tagSets) == IS_ARRAY) {
131+
bson_t* tags = bson_new();
132+
133+
php_phongo_read_preference_prep_tagsets(*tagSets TSRMLS_CC);
134+
php_phongo_zval_to_bson(*tagSets, PHONGO_BSON_NONE, (bson_t*) tags, NULL TSRMLS_CC);
135+
136+
if (!php_phongo_read_preference_tags_are_valid(tags)) {
137+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" array field to have zero or more documents", ZSTR_VAL(php_phongo_writeconcern_ce->name));
138+
bson_destroy(tags);
139+
goto failure;
140+
}
141+
142+
if (!bson_empty(tags) && (mongoc_read_prefs_get_mode(intern->read_preference) == MONGOC_READ_PRIMARY)) {
143+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" array field to not be present with \"primary\" mode", ZSTR_VAL(php_phongo_writeconcern_ce->name));
144+
bson_destroy(tags);
145+
goto failure;
146+
}
147+
148+
mongoc_read_prefs_set_tags(intern->read_preference, tags);
149+
bson_destroy(tags);
150+
} else {
151+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"tags\" field to be array", ZSTR_VAL(php_phongo_writeconcern_ce->name));
152+
goto failure;
153+
}
154+
}
155+
156+
if (zend_hash_find(props, "maxStalenessSeconds", sizeof("maxStalenessSeconds"), (void**) &maxStalenessSeconds) == SUCCESS) {
157+
if (Z_TYPE_PP(maxStalenessSeconds) == IS_LONG) {
158+
if (Z_LVAL_PP(maxStalenessSeconds) < MONGOC_SMALLEST_MAX_STALENESS_SECONDS) {
159+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" integer field to be >= %d", ZSTR_VAL(php_phongo_writeconcern_ce->name), MONGOC_SMALLEST_MAX_STALENESS_SECONDS);
160+
goto failure;
161+
}
162+
if (Z_LVAL_PP(maxStalenessSeconds) > INT32_MAX) {
163+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" integer field to be <= %" PRId32, ZSTR_VAL(php_phongo_writeconcern_ce->name), INT32_MAX);
164+
goto failure;
165+
}
166+
if (mongoc_read_prefs_get_mode(intern->read_preference) == MONGOC_READ_PRIMARY) {
167+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" array field to not be present with \"primary\" mode", ZSTR_VAL(php_phongo_writeconcern_ce->name));
168+
goto failure;
169+
}
170+
171+
mongoc_read_prefs_set_max_staleness_seconds(intern->read_preference, Z_LVAL_PP(maxStalenessSeconds));
172+
} else {
173+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"maxStalenessSeconds\" field to be integer", ZSTR_VAL(php_phongo_writeconcern_ce->name));
174+
goto failure;
175+
}
176+
}
177+
#endif
178+
179+
return true;
180+
181+
failure:
182+
mongoc_read_prefs_destroy(intern->read_preference);
183+
intern->read_preference = NULL;
184+
return false;
185+
} /* }}} */
186+
31187
/* {{{ proto void MongoDB\Driver\ReadPreference::__construct(int|string $mode[, array $tagSets = array()[, array $options = array()]])
32188
Constructs a new ReadPreference */
33189
static PHP_METHOD(ReadPreference, __construct)
@@ -131,6 +287,26 @@ static PHP_METHOD(ReadPreference, __construct)
131287
}
132288
} /* }}} */
133289

290+
/* {{{ proto void MongoDB\BSON\ReadPreference::__set_state(array $properties)
291+
*/
292+
static PHP_METHOD(ReadPreference, __set_state)
293+
{
294+
php_phongo_readpreference_t* intern;
295+
HashTable* props;
296+
zval* array;
297+
298+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
299+
RETURN_FALSE;
300+
}
301+
302+
object_init_ex(return_value, php_phongo_readpreference_ce);
303+
304+
intern = Z_READPREFERENCE_OBJ_P(return_value);
305+
props = Z_ARRVAL_P(array);
306+
307+
php_phongo_readpreference_init_from_hash(intern, props TSRMLS_CC);
308+
} /* }}} */
309+
134310
/* {{{ proto integer MongoDB\Driver\ReadPreference::getMaxStalenessSeconds()
135311
Returns the ReadPreference maxStalenessSeconds value */
136312
static PHP_METHOD(ReadPreference, getMaxStalenessSeconds)
@@ -302,12 +478,17 @@ ZEND_BEGIN_ARG_INFO_EX(ai_ReadPreference___construct, 0, 0, 1)
302478
ZEND_ARG_ARRAY_INFO(0, options, 1)
303479
ZEND_END_ARG_INFO()
304480

481+
ZEND_BEGIN_ARG_INFO_EX(ai_ReadPreference___set_state, 0, 0, 1)
482+
ZEND_ARG_ARRAY_INFO(0, properties, 0)
483+
ZEND_END_ARG_INFO()
484+
305485
ZEND_BEGIN_ARG_INFO_EX(ai_ReadPreference_void, 0, 0, 0)
306486
ZEND_END_ARG_INFO()
307487

308488
static zend_function_entry php_phongo_readpreference_me[] = {
309489
/* clang-format off */
310490
PHP_ME(ReadPreference, __construct, ai_ReadPreference___construct, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
491+
PHP_ME(ReadPreference, __set_state, ai_ReadPreference___set_state, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
311492
PHP_ME(ReadPreference, getMaxStalenessSeconds, ai_ReadPreference_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
312493
PHP_ME(ReadPreference, getMode, ai_ReadPreference_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
313494
PHP_ME(ReadPreference, getTagSets, ai_ReadPreference_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
@@ -356,7 +537,7 @@ static phongo_create_object_retval php_phongo_readpreference_create_object(zend_
356537
#else
357538
{
358539
zend_object_value retval;
359-
retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t) zend_objects_destroy_object, php_phongo_readpreference_free_object, NULL TSRMLS_CC);
540+
retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t) zend_objects_destroy_object, php_phongo_readpreference_free_object, NULL TSRMLS_CC);
360541
retval.handlers = &php_phongo_handler_readpreference;
361542

362543
return retval;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
MongoDB\Driver\ReadPreference::__set_state()
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
$tests = [
9+
[ 'mode' => 'primary' ],
10+
[ 'mode' => 'primaryPreferred' ],
11+
[ 'mode' => 'secondary' ],
12+
[ 'mode' => 'secondaryPreferred' ],
13+
[ 'mode' => 'nearest' ],
14+
[ 'mode' => 'secondary', 'tags' => [['dc' => 'ny']] ],
15+
[ 'mode' => 'secondary', 'tags' => [['dc' => 'ny'], ['dc' => 'sf', 'use' => 'reporting'], []] ],
16+
[ 'mode' => 'secondary', 'maxStalenessSeconds' => 1000 ],
17+
18+
];
19+
20+
foreach ($tests as $fields) {
21+
var_export(MongoDB\Driver\ReadPreference::__set_state($fields));
22+
echo "\n\n";
23+
}
24+
25+
?>
26+
===DONE===
27+
<?php exit(0); ?>
28+
--EXPECTF--
29+
MongoDB\Driver\ReadPreference::__set_state(array(
30+
'mode' => 'primary',
31+
))
32+
33+
MongoDB\Driver\ReadPreference::__set_state(array(
34+
'mode' => 'primaryPreferred',
35+
))
36+
37+
MongoDB\Driver\ReadPreference::__set_state(array(
38+
'mode' => 'secondary',
39+
))
40+
41+
MongoDB\Driver\ReadPreference::__set_state(array(
42+
'mode' => 'secondaryPreferred',
43+
))
44+
45+
MongoDB\Driver\ReadPreference::__set_state(array(
46+
'mode' => 'nearest',
47+
))
48+
49+
MongoDB\Driver\ReadPreference::__set_state(array(
50+
'mode' => 'secondary',
51+
'tags' =>
52+
array (
53+
0 =>
54+
%Sarray(
55+
'dc' => 'ny',
56+
%S),
57+
),
58+
))
59+
60+
MongoDB\Driver\ReadPreference::__set_state(array(
61+
'mode' => 'secondary',
62+
'tags' =>
63+
array (
64+
0 =>
65+
%Sarray(
66+
'dc' => 'ny',
67+
%S),
68+
1 =>
69+
%Sarray(
70+
'dc' => 'sf',
71+
'use' => 'reporting',
72+
%S),
73+
2 =>
74+
%Sarray(
75+
%S),
76+
),
77+
))
78+
79+
MongoDB\Driver\ReadPreference::__set_state(array(
80+
'mode' => 'secondary',
81+
'maxStalenessSeconds' => 1000,
82+
))
83+
84+
===DONE===
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
MongoDB\Driver\ReadPreference::__set_state() requires correct data types and values
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
echo throws(function() {
9+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'furthest']);
10+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
11+
12+
echo throws(function() {
13+
MongoDB\Driver\ReadPreference::__set_state(['mode' => M_PI]);
14+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
15+
16+
17+
echo throws(function() {
18+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'secondary', 'tags' => -1]);
19+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
20+
21+
echo throws(function() {
22+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'secondary', 'tags' => [ 42 ] ]);
23+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
24+
25+
echo throws(function() {
26+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'primary', 'tags' => [['dc' => 'ny']]]);
27+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
28+
29+
30+
echo throws(function() {
31+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'secondary', 'maxStalenessSeconds' => 1]);
32+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
33+
34+
echo throws(function() {
35+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'primary', 'maxStalenessSeconds' => 100]);
36+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
37+
38+
?>
39+
===DONE===
40+
<?php exit(0); ?>
41+
--EXPECT--
42+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
43+
MongoDB\Driver\ReadPreference initialization requires specific values for "mode" string field
44+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
45+
MongoDB\Driver\ReadPreference initialization requires "mode" field to be string
46+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
47+
MongoDB\Driver\WriteConcern initialization requires "tags" field to be array
48+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
49+
MongoDB\Driver\WriteConcern initialization requires "tags" array field to have zero or more documents
50+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
51+
MongoDB\Driver\WriteConcern initialization requires "tags" array field to not be present with "primary" mode
52+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
53+
MongoDB\Driver\WriteConcern initialization requires "maxStalenessSeconds" integer field to be >= 90
54+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
55+
MongoDB\Driver\WriteConcern initialization requires "maxStalenessSeconds" array field to not be present with "primary" mode
56+
===DONE===
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
MongoDB\Driver\ReadPreference::__set_state() requires correct data types and values
3+
--SKIPIF--
4+
<?php if (8 !== PHP_INT_SIZE) { die('skip Only for 64-bit platform'); } ?>
5+
--FILE--
6+
<?php
7+
8+
require_once __DIR__ . '/../utils/tools.php';
9+
10+
echo throws(function() {
11+
MongoDB\Driver\ReadPreference::__set_state(['mode' => 'secondary', 'maxStalenessSeconds' => pow(2, 32) + 1]);
12+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
13+
14+
?>
15+
===DONE===
16+
<?php exit(0); ?>
17+
--EXPECT--
18+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
19+
MongoDB\Driver\WriteConcern initialization requires "maxStalenessSeconds" integer field to be <= 2147483647
20+
===DONE===

tests/writeConcern/writeconcern-set_state_error-002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ MongoDB\Driver\WriteConcern::__set_state() requires correct data types and value
88
require_once __DIR__ . '/../utils/tools.php';
99

1010
echo throws(function() {
11-
MongoDB\Driver\WriteConcern::__set_state(['wtimeout' => pow(2, 32) + 1]);
11+
MongoDB\Driver\WriteConcern::__set_state(['wtimeout' => 2147483648]);
1212
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1313

1414
?>

0 commit comments

Comments
 (0)